This commit is contained in:
fanchenio 2023-11-03 15:17:45 +08:00
parent d448342d13
commit 1eeee5630e
185 changed files with 39296 additions and 38433 deletions

2
.env.production Normal file
View File

@ -0,0 +1,2 @@
VITE_BETTER_SQLITE3_BINDING=.\resources\app.asar\native\better_sqlite3.node
VITE_INSTALL=false

38
.gitignore vendored
View File

@ -1,28 +1,38 @@
.DS_Store # Logs
node_modules logs
/dist *.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
dist-electron
native
release
*.local
.env
# Editor directories and files # Editor directories and files
.vscode/.debug.env
.idea .idea
.vscode .DS_Store
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
#Electron-builder output # lockfile
/dist_electron package-lock.json
pnpm-lock.yaml
yarn.lock
/build # VSCode
.history
# Rust
target

View File

@ -1,3 +0,0 @@
{
"printWidth": 160,
}

23
.vscode/.debug.script.mjs vendored Normal file
View File

@ -0,0 +1,23 @@
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import { spawn } from 'node:child_process'
const pkg = createRequire(import.meta.url)('../package.json')
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// write .debug.env
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
// bootstrap
spawn(
// TODO: terminate `npm run dev` when Debug exits.
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', 'dev'],
{
stdio: 'inherit',
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
},
)

6
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin"
]
}

53
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,53 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"compounds": [
{
"name": "Debug App",
"preLaunchTask": "Before Debug",
"configurations": [
"Debug Main Process",
"Debug Renderer Process"
],
"presentation": {
"hidden": false,
"group": "",
"order": 1
},
"stopAll": true
}
],
"configurations": [
{
"name": "Debug Main Process",
"type": "node",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
},
"runtimeArgs": [
"--remote-debugging-port=9229",
"."
],
"envFile": "${workspaceFolder}/.vscode/.debug.env",
"console": "integratedTerminal"
},
{
"name": "Debug Renderer Process",
"port": 9229,
"request": "attach",
"type": "chrome",
"timeout": 60000,
"skipFiles": [
"<node_internals>/**",
"${workspaceRoot}/node_modules/**",
"${workspaceRoot}/dist-electron/**",
// Skip files in host(VITE_DEV_SERVER_URL)
"http://127.0.0.1:3344/**"
]
},
]
}

13
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,13 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.tsc.autoDetect": "off",
"json.schemas": [
{
"fileMatch": [
"/*electron-builder.json5",
"/*electron-builder.json"
],
"url": "https://json.schemastore.org/electron-builder"
}
]
}

31
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,31 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Before Debug",
"type": "shell",
"command": "node .vscode/.debug.script.mjs",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"fileLocation": "relative",
"pattern": {
// TODO: correct "regexp"
"regexp": "^([a-zA-Z]\\:\/?([\\w\\-]\/?)+\\.\\w+):(\\d+):(\\d+): (ERROR|WARNING)\\: (.*)$",
"file": 1,
"line": 3,
"column": 4,
"code": 5,
"message": 6
},
"background": {
"activeOnStart": true,
"beginsPattern": "^.*VITE v.* ready in \\d* ms.*$",
"endsPattern": "^.*\\[startup\\] Electron App.*$"
}
}
}
]
}

748
Cargo.lock generated Normal file
View File

@ -0,0 +1,748 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clipboard-win"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
dependencies = [
"error-code",
"str-buf",
"winapi",
]
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "ctor"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e366bff8cd32dd8754b0991fb66b279dc48f598c3a18914852a6673deef583"
dependencies = [
"quote",
"syn 2.0.38",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "error-code"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21"
dependencies = [
"libc",
"str-buf",
]
[[package]]
name = "exr"
version = "1.71.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
dependencies = [
"bit_field",
"flume",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "fdeflate"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868"
dependencies = [
"simd-adler32",
]
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "flume"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
dependencies = [
"spin",
]
[[package]]
name = "gif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "half"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
dependencies = [
"crunchy",
]
[[package]]
name = "image"
version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"exr",
"gif",
"jpeg-decoder",
"num-rational",
"num-traits",
"png",
"qoi",
"tiff",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
dependencies = [
"rayon",
]
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
"simd-adler32",
]
[[package]]
name = "napi"
version = "2.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd063c93b900149304e3ba96ce5bf210cd4f81ef5eb80ded0d100df3e85a3ac0"
dependencies = [
"bitflags 2.4.1",
"ctor",
"napi-derive",
"napi-sys",
"once_cell",
]
[[package]]
name = "napi-build"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e"
[[package]]
name = "napi-derive"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da1c6a8fa84d549aa8708fcd062372bf8ec6e849de39016ab921067d21bde367"
dependencies = [
"cfg-if",
"convert_case",
"napi-derive-backend",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "napi-derive-backend"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20bbc7c69168d06a848f925ec5f0e0997f98e8c8d4f2cc30157f0da51c009e17"
dependencies = [
"convert_case",
"once_cell",
"proc-macro2",
"quote",
"regex",
"semver",
"syn 1.0.109",
]
[[package]]
name = "napi-sys"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3"
dependencies = [
"libloading",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "png"
version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "proc-macro2"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rust"
version = "0.0.0"
dependencies = [
"base64",
"clipboard-win",
"image",
"napi",
"napi-build",
"napi-derive",
"serde",
"serde_json",
"static_vcruntime",
"windows",
]
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "serde_json"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "smallvec"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "static_vcruntime"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "954e3e877803def9dc46075bf4060147c55cd70db97873077232eae0269dc89b"
[[package]]
name = "str-buf"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tiff"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]

View File

@ -1,37 +1,70 @@
# Dawn Launcher # 更新
Windows快捷启动工具帮助您整理杂乱无章的桌面分门别类管理您的桌面快捷方式让您的桌面保持干净整洁。
消失了三个多月,我将`Dawn Launcher`代码进行重构,因为一开始没有开发`NodeJS`和`Electron`的经验,`Vue`也写的不好,代码写的有些潦草,所以进行了代码重构,新版使用了`Electron26 + Vite + Vue3 + TS`UI 框架使用了`Naive`,关于原生 API 方面,我从`C++`切换到了 `Rust`,数据库从`electron-store`切换到了`SQLite3`,语言也新增了`英语`。
# Dawn Launcher
`Windows`快捷启动工具,帮助您整理杂乱无章的桌面,分门别类管理您的桌面快捷方式,让您的桌面保持干净整洁。
支持关联文件夹(实时同步文件夹内容)、快速搜索、相对路径(便携路径)、扫描本机开始菜单、本地扫描本机 Appx 应用列表、添加网址并一键获取网址信息。
# 技术栈
`Electron + Vite + Vue3 + TS`
支持相对路径便携路径支持扫描本机开始菜单、本地Appx应用列表添加项目、支持多项目添加一次启动多个项目、支持添加网址并一键获取网址信息。
# 开发框架
Electron
# 支持平台 # 支持平台
Windows
`Windows(10/11)`
# 编译步骤 # 编译步骤
1. 需要配置 node-gyp 环境,这个可以自行搜索一下。
2. yarn install 安装依赖。 1. 首先需要安装`node-gyp`,编译本地模块需要用到。
3. node-gyp rebuild --arch=x64 或 node-gyp rebuild --arch=ia32 编译64位或32位DLL。 2. 然后运行`yarn install`安装项目依赖(如果修改了`Rust`代码也需要重新运行`yarn install`)。
4. yarn electron:serve 本地运行项目。 3. `yarn run dev`本地运行项目。
5. yarn electron:build 打包项目打包64位和32位的时候需要分两次打包将 vue.config.js 配置中的 pluginOptions.electronBuilder.builderOptions.win.target.arch 修改为x64或者ia32。 4. `yarn run build`打包项目。
5. 便携版和安装版需要分两次打包,通过修改`.env.production`中的`VITE_INSTALL``true`为安装版,`false`为便携版。
# 官网 # 官网
[dawnlauncher.com](https://dawnlauncher.com/) [dawnlauncher.com](https://dawnlauncher.com/)
# 捐赠 # 捐赠
![微信](/images/wechat.png) ![微信](/images/wechat.png)
![支付宝](/images/alipay.png) ![支付宝](/images/alipay.png)
# 界面 # 界面
![界面](/images/界面.png)
![界面](/images/soft1.png)
## 子分类 ## 子分类
![子分类](/images/子分类.png)
![子分类](/images/soft2.png)
## 自定义主题 ## 自定义主题
![自定义主题](/images/自定义主题.png)
![自定义主题](/images/soft3.png)
## 自定义背景 ## 自定义背景
![自定义背景](/images/自定义背景.png)
![自定义背景](/images/soft4.png)
## 快速搜索 ## 快速搜索
![快速搜索](/images/快速搜索.gif)
![快速搜索](/images/soft5.png)
## 一键获取网址信息 ## 一键获取网址信息
![一键获取网址信息](/images/一键获取网址信息.gif)
![一键获取网址信息](/images/soft6.webp)
## 相对路径(便携路径) ## 相对路径(便携路径)
![相对路径(便携路径)](/images/相对路径.png)
![相对路径(便携路径)](/images/soft7.png)
## 关联文件夹 ## 关联文件夹
![关联文件夹](/images/关联文件夹.gif)
![关联文件夹](/images/soft8.webp)
# License # License
Apache License Version 2.0 Apache License Version 2.0

View File

@ -1,4 +0,0 @@
module.exports = {
presets: ["@vue/cli-plugin-babel/preset"],
plugins: [],
};

View File

@ -1,14 +0,0 @@
{
"targets": [
{
"target_name": "api",
"cflags!": [ "-fno-exceptions" ],
"cflags_cc!": [ "-fno-exceptions" ],
"sources": [ "./src/main/api/api.cc" ],
"include_dirs": [
"<!@(node -p \"require('node-addon-api').include\")"
],
'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
}
]
}

6
build.rs Normal file
View File

@ -0,0 +1,6 @@
extern crate napi_build;
fn main() {
static_vcruntime::metabuild();
napi_build::setup();
}

37
cargo.toml Normal file
View File

@ -0,0 +1,37 @@
[package]
name = "rust"
version = "0.0.0"
build = "build.rs"
edition = "2021"
[lib]
path = "rust/lib.rs"
crate-type = ["cdylib"]
[dependencies]
napi = { version = "2", features = ["napi4"] }
napi-derive = "2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
image = "0.24.6"
base64 = "0.21.2"
clipboard-win = "4.5.0"
[dependencies.windows]
version = "0.48"
features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_Com",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
"Win32_Storage_FileSystem",
"Win32_System_SystemInformation",
"Win32_System_Environment",
"Win32_UI_Input_Ime",
"Win32_Globalization",
]
[build-dependencies]
napi-build = "2"
static_vcruntime = "2.0"

3
commons/data/languages.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
// 无用为了tsc编译通过
export function getLanguage(language: string | null): any;

934
commons/data/languages.ts Normal file
View File

@ -0,0 +1,934 @@
// 简体中文
let simplifiedChinese = {
about: "关于",
add: "新增",
address: "地址",
aggregateClassification: "聚合分类",
aggregateClassificationPrompt1:
"聚合分类可以将所有分类下的项目聚合到一起,按照设定的排序方式、项目数量进行排序和显示。",
aggregateClassificationPrompt2: "当前分类为父级分类,不能设置聚合分类。",
aggregateClassificationPrompt3:
"当前设置排序为按打开次数,需开启设置-项目-记录打开次数。",
aggregateClassificationPrompt4:
"设置聚合分类会清空原分类下所有项目,请谨慎操作,是否确认?",
align: "对齐",
all: "全部",
altNumberKey: "Alt + 数字键",
alwaysCenter: "永远居中",
alwaysTop: "永远置顶",
appearance: "外观",
appx: "Appx",
associateFolder: "关联文件夹",
associateFolderPrompt1:
"关联文件夹可以实时监控指定文件夹内的变化并同步到对应分类中。",
associateFolderPrompt2: "需要隐藏的文件/文件夹名称,多个按英文逗号分割",
associateFolderPrompt3: "当前分类为父级分类,不能设置关联文件夹。",
associateFolderPrompt4: "目标路径不存在。",
associateFolderPrompt5: "目标路径必须是文件夹。",
associateFolderPrompt6:
"设置关联文件夹会清空原分类下所有项目,请谨慎操作,是否确认?",
associateFolderPrompt7:
"目标路径为空,当前分类将会自动转换为普通分类,是否确认?",
autoSwitchClassification: "项目列表滚动到底部或顶部时自动切换分类",
backgroundIcon: "背景图标",
backgroundImage: "背景图",
backgroundImageMode: "背景图模式",
backgroundImagePostion: "背景图定位",
backgroundImageTransparent: "背景图透明",
backgroundTransparent: "背景透明",
backup: "备份",
backupRestoreData: "备份/还原数据",
backupRestoreDataPrompt:
"如果要还原1.2.3及之前版本的备份数据请在还原时选择导入JSON格式的文件。",
batchConvertAbsolutePath: "转为绝对路径",
batchConvertRelativePath: "转为相对路径",
batchCopyTo: "批量复制到",
batchDelete: "批量删除",
batchDeletePrompt: "是否批量删除项目?",
batchMoveTo: "批量移动到",
batchOperation: "批量操作",
batchRefreshIcon: "批量刷新图标",
borderColor: "边框颜色",
bottom: "底部",
byInitialLetter: "按首字母",
byLastOpen: "按最后打开",
byOpenNumber: "按打开次数",
calculator: "计算器",
cancel: "取消",
cancelBatchOperation: "取消批量操作",
center: "居中",
checkCode: "校验代码",
checkInvalidItem: "检查无效项目",
checkUpdates: "检查更新",
checkUpdatesPrompt1: '检查到有新版本,点击"确定"跳转官网下载最新版本。',
checkUpdatesPrompt2: "当前已经是最新版本。",
checkUpdatesPrompt3: "检查更新失败,请确认网络。",
classification: "分类",
clear: "清空",
close: "关闭",
colon: "",
colonKeywordSpace: "冒号 + 关键字 + 空格",
color: "颜色",
columnNumber: "列数",
commandPrompt: "命令提示符",
computer: "计算机",
computerManagement: "计算机管理",
controlPanel: "控制面板",
convertAbsolutePath: "转为绝对路径",
convertRelativePath: "转为相对路径",
copyFullPath: "复制完整路径",
copyTo: "复制到",
createShortcut: "创建快捷方式",
ctrlNumberKey: "Ctrl + 数字键",
default: "默认",
defaultIcon: "默认图标",
delayDisplay: "延迟显示",
delayHide: "延迟隐藏",
delete: "删除",
deleteClassificationPrompt: "是否删除当前分类?",
deleteIcon: "删除图标",
deleteItemPrompt: "是否删除当前项目?",
description: "描述",
display: "显示",
displayMainWindow: "显示主界面",
documents: "文档",
doubleClickOpen: "双击打开",
doubleClickTaskbar: "双击任务栏",
downloadImagePrompt1: "下载图片失败。",
downloadImagePrompt2: "链接不是图片格式。",
dontPromptAgain: "不再提示",
edgeAutoHide: "停靠在桌面边缘时自动隐藏",
edgeDock: "边缘吸附",
edit: "编辑",
editClassification: "编辑分类",
editItem: "编辑项目",
editSubclassification: "编辑子分类",
emptyRecycleBin: "清空回收站",
enable: "启用",
english: "英语",
excludeSearch: "排除搜索",
exit: "退出",
explorerMenu: "资源管理器菜单",
exportIcon: "导出图标",
extraLarge: "超大",
feedback: "反馈",
file: "文件",
fileExplorer: "文件资源管理器",
fixedClassification: "固定分类",
fixedIcon: "固定图标",
fixedPosition: "固定位置",
folder: "文件夹",
folderPath: "文件夹路径",
fontLineHeight: "字体行高",
fontMainColor: "字体主色",
fontSecondaryColor: "字体副色",
fontShadow: "字体阴影",
fontSize: "字体大小",
fontWeight: "字体粗细",
functions: "功能",
general: "常规",
getIcon: "获取图标",
getURLInformation: "获取网址信息",
globalShortcutKey: "全局快捷键",
hiddenItems: "隐藏项",
hideEllipses: "隐藏省略号",
hideLoseFocus: "失去焦点后隐藏",
hideName: "隐藏名称",
hideTray: "隐藏托盘图标",
hideWindowCollapseSubClassification: "隐藏窗口时收起子分类",
history: "历史记录",
hover: "悬停",
icon: "图标",
imageLink: "图片链接",
item: "项目",
itemAddEditPrompt1: "选中固定图标后,当前项目不参与刷新图标。",
itemAddEditPrompt2: "无特殊需求为空即可",
itemAddEditPrompt3: "添加成功。",
itemAddEditPrompt4: "目标不存在。",
itemAddEditPrompt5: "获取网址信息失败。",
itemList: "项目列表",
keyword: "关键字",
keywordSpace: "关键字 + 空格",
language: "语言",
large: "大",
layout: "布局",
layoutListModeTakeEffect: '布局为"列表"模式下生效。',
layoutTileModeTakeEffect: '布局为"平铺"模式下生效。',
left: "左侧",
lineNumber: "行数",
list: "列表",
lock: "锁定",
lockClassification: "锁定分类",
lockItem: "锁定项目",
lockSize: "锁定尺寸",
mainColor: "主色",
matchCondition: "匹配条件",
medium: "中",
middleClick: "中键单击",
millisecond: "毫秒",
mode: "模式",
modifyIcon: "修改图标",
mouseHover: "鼠标悬停",
mouseWheel: "鼠标滚轮",
moveTo: "移动到",
multiItems: "多项目",
name: "名称",
network: "网络",
networkIcon: "网络图标",
networkIconPrompt1: "支持JPG/JPEG/GIF/PNG/ICO/SVG/WEBP格式图片。",
networkShareCenter: "网络和共享中心",
newClassification: "新建分类",
newClassificationName: "新分类",
newItem: "新建项目",
newSubclassification: "新建子分类",
none: "无",
noRepeat: "不重复",
normal: "普通",
notDisturb: "勿扰模式",
notDisturbPrompt:
"开启勿扰模式后计算机在游戏、应用全屏模式下不会弹出Dawn Launcher窗口。",
notFoundFile: "找不到指定的文件",
notFoundFolder: "找不到指定的文件夹",
number: "数量",
numberKey: "数字键",
officialWebsite: "官方网站",
ok: "确定",
open: "打开",
openAfterHideMainInterface: "打开后隐藏主界面",
openAfterHideQuickSearchWindow: "打开后隐藏窗口",
openFileLocation: "打开文件所在位置",
openNow: "仅剩一项时立即打开",
openNumber: "打开次数",
parameters: "参数",
password: "密码",
pasteIcon: "粘贴图标",
powerOptions: "电源选项",
programsFeatures: "程序和功能",
proxy: "代理",
proxyPrompt:
"仅支持HTTP代理填写“地址”时需要带通信协议和端口例如http://127.0.0.1:7890如果没有用户名和密码为空即可。",
quickSearch: "快速搜索",
recordOpenNumber: "记录打开次数",
recycleBin: "回收站",
refreshIcon: "刷新图标",
registryEditor: "注册表编辑器",
remark: "备注",
rememberSelectionState: "记住选择状态",
repeat: "重复",
resourceMonitor: "资源监视器",
restart: "重启",
restore: "还原",
restoreDataPrompt: "还原数据失败。",
rewardSponsor: "打赏&赞助",
right: "右侧",
roundedCorners: "圆角",
runAsAdministrator: "以管理员身份运行",
runSystemStartup: "开机启动",
save: "保存",
search: "搜索",
searchSource: "搜索源",
secondaryColor: "副色",
select: "选择",
selectAll: "全选",
selectItem: "选择项目",
services: "服务",
setClassificationIcon: "设置分类图标",
setIcon: "设置图标",
settings: "设置",
shortcutKey: "快捷键",
shortcutKeyPrompt1: "快捷键不完整,请完善快捷键。",
shortcutKeyPrompt2: (value: string) => {
return '与"' + value + '"分类快捷键产生冲突,请重新设置。';
},
shortcutKeyPrompt3: (value: string) => {
return '与"' + value + '"项目快捷键产生冲突,请重新设置。';
},
shortcutKeyPrompt4:
'与"设置-常规"中的"显示/隐藏"快捷键产生冲突,请重新设置。',
shortcutKeyPrompt5: '与"设置-常规"中的"搜索"快捷键产生冲突,请重新设置。',
shortcutKeyPrompt6:
'与"设置-快速搜索"中的"显示/隐藏"快捷键产生冲突,请重新设置。',
show: "显示",
showFollowMousePosition: "显示时跟随鼠标位置",
showHide: "显示/隐藏",
showOnlyFiles: "只显示文件",
showOnlyFolders: "只显示文件夹",
shutdown: "关机",
simplifiedChinese: "简体中文",
size: "尺寸",
sleep: "睡眠",
small: "小",
sort: "排序",
startLocation: "起始位置",
startMenu: "开始菜单",
startup: "启动",
startupTray: "启动后最小化到系统托盘",
subclassification: "子分类",
svgIcon: "SVG图标",
svgIconPrompt1: "输入SVG代码后需要先点击“校验代码”按钮。",
switch: "切换",
switchClassificationCollapseOtherSubClassification:
"切换分类时收起其他子分类",
switchEnglish: "显示窗口时将输入法切换为英文模式",
system: "系统",
target: "目标",
taskManager: "任务管理器",
theme: "主题",
timeInterval: "时间间隔",
tile: "平铺",
title: "标题",
top: "顶部",
traditionalChinese: "繁体中文",
turnOffMonitor: "关闭显示器",
unlockClassification: "解锁分类",
unlockItem: "解锁项目",
uploadIcon: "上传图标",
url: "网址",
useItemOpen: "从程序外拖动文件到项目图标上时用此项目打开文件",
useProxy: "使用代理",
useQuickSearch: "使用快速搜索",
username: "用户名",
update: "更新",
webSearch: "网络搜索",
webSearchModePrompt1:
'输入"冒号 + 关键字 + 空格"或"关键字 + 空格"使用网络搜索,例如使用谷歌搜索,输入":g"或"g",然后按下空格键,进入网络搜索模式。',
webSearchModePrompt2: "{w}为搜索关键字。",
width: "宽度",
window: "窗口",
zoom: "缩放",
};
let traditionalChinese = {
about: "關於",
add: "新增",
address: "地址",
aggregateClassification: "聚合分類",
aggregateClassificationPrompt1:
"聚合分類可以將所有分類下的項目聚合到一起,按照設定的排序方式、項目數量進行排序和顯示。",
aggregateClassificationPrompt2: "當前分類為父級分類,不能設置聚合分類。",
aggregateClassificationPrompt3:
"當前設置排序為按打開次數,需開啟設置-項目-記錄打開次數。",
aggregateClassificationPrompt4:
"設置聚合分類會清空原分類下所有項目,請謹慎操作,是否確認?",
align: "對齊",
all: "全部",
altNumberKey: "Alt + 數字鍵",
alwaysCenter: "永遠居中",
alwaysTop: "永遠置頂",
appearance: "外觀",
appx: "Appx",
associateFolder: "關聯文件夾",
associateFolderPrompt1:
"關聯文件夾可以實時監控指定文件夾內的變化並同步到對應分類中。",
associateFolderPrompt2: "需要隱藏的文件/文件夾名稱,多個按英文逗號分割",
associateFolderPrompt3: "當前分類為父級分類,不能設置關聯文件夾。",
associateFolderPrompt4: "目標路徑不存在。",
associateFolderPrompt5: "目標路徑必須是文件夾。",
associateFolderPrompt6:
"設置關聯文件夾會清空原分類下所有項目,請謹慎操作,是否確認?",
associateFolderPrompt7:
"目標路徑為空,當前分類將會自動轉換為普通分類,是否確認?",
autoSwitchClassification: "項目列表滾動到底部或頂部時自動切換分類",
backgroundIcon: "背景圖標",
backgroundImage: "背景圖",
backgroundImageMode: "背景圖模式",
backgroundImagePostion: "背景圖定位",
backgroundImageTransparent: "背景圖透明",
backgroundTransparent: "背景透明",
backup: "備份",
backupRestoreData: "備份/還原數據",
backupRestoreDataPrompt:
"如果要還原1.2.3及之前版本的備份數據請在還原時選擇導入JSON格式的文件。",
batchConvertAbsolutePath: "轉為絕對路徑",
batchConvertRelativePath: "轉為相對路徑",
batchCopyTo: "批量復製到",
batchDelete: "批量刪除",
batchDeletePrompt: "是否批量刪除項目?",
batchMoveTo: "批量移動到",
batchOperation: "批量操作",
batchRefreshIcon: "批量刷新圖標",
borderColor: "邊框顏色",
bottom: "底部",
byInitialLetter: "按首字母",
byLastOpen: "按最後打開",
byOpenNumber: "按打開次數",
calculator: "計算器",
cancel: "取消",
cancelBatchOperation: "取消批量操作",
center: "居中",
checkCode: "校驗代碼",
checkInvalidItem: "檢查無效項目",
checkUpdates: "檢查更新",
checkUpdatesPrompt1: '檢查到有新版本,點擊"確定"跳轉官網下載最新版本。',
checkUpdatesPrompt2: "當前已經是最新版本。",
checkUpdatesPrompt3: "檢查更新失敗,請確認網絡。",
classification: "分類",
clear: "清空",
close: "關閉",
colon: "",
colonKeywordSpace: "冒號 + 關鍵字 + 空格",
color: "顏色",
columnNumber: "列數",
commandPrompt: "命令提示符",
computer: "計算機",
computerManagement: "計算機管理",
controlPanel: "控製面板",
convertAbsolutePath: "轉為絕對路徑",
convertRelativePath: "轉為相對路徑",
copyFullPath: "復製完整路徑",
copyTo: "復製到",
createShortcut: "創建快捷方式",
ctrlNumberKey: "Ctrl + 數字鍵",
default: "默認",
defaultIcon: "默認圖標",
delayDisplay: "延遲顯示",
delayHide: "延遲隱藏",
delete: "刪除",
deleteClassificationPrompt: "是否刪除當前分類?",
deleteIcon: "刪除圖標",
deleteItemPrompt: "是否刪除當前項目?",
description: "描述",
display: "顯示",
displayMainWindow: "顯示主界面",
documents: "文檔",
doubleClickOpen: "雙擊打開",
doubleClickTaskbar: "雙擊任務欄",
downloadImagePrompt1: "下載圖片失敗。",
downloadImagePrompt2: "鏈接不是圖片格式。",
dontPromptAgain: "不再提示",
edgeAutoHide: "停靠在桌面邊緣時自動隱藏",
edgeDock: "邊緣吸附",
edit: "編輯",
editClassification: "編輯分類",
editItem: "編輯項目",
editSubclassification: "編輯子分類",
emptyRecycleBin: "清空回收站",
enable: "啟用",
english: "英語",
excludeSearch: "排除搜索",
exit: "退出",
explorerMenu: "資源管理器菜單",
exportIcon: "導出圖標",
extraLarge: "超大",
feedback: "反饋",
file: "文件",
fileExplorer: "文件資源管理器",
fixedClassification: "固定分類",
fixedIcon: "固定圖標",
fixedPosition: "固定位置",
folder: "文件夾",
folderPath: "文件夾路徑",
fontLineHeight: "字體行高",
fontMainColor: "字體主色",
fontSecondaryColor: "字體副色",
fontShadow: "字體陰影",
fontSize: "字體大小",
fontWeight: "字體粗細",
functions: "功能",
general: "常規",
getIcon: "獲取圖標",
getURLInformation: "獲取網址信息",
globalShortcutKey: "全局快捷鍵",
hiddenItems: "隱藏項",
hideEllipses: "隱藏省略號",
hideLoseFocus: "失去焦點後隱藏",
hideName: "隱藏名稱",
hideTray: "隱藏托盤圖標",
hideWindowCollapseSubClassification: "隱藏窗口時收起子分類",
history: "歷史記錄",
hover: "懸停",
icon: "圖標",
imageLink: "圖片鏈接",
item: "項目",
itemAddEditPrompt1: "選中固定圖標後,當前項目不參與刷新圖標。",
itemAddEditPrompt2: "無特殊需求為空即可",
itemAddEditPrompt3: "添加成功。",
itemAddEditPrompt4: "目標不存在。",
itemAddEditPrompt5: "獲取網址信息失敗。",
itemList: "項目列表",
keyword: "關鍵字",
keywordSpace: "關鍵字 + 空格",
language: "語言",
large: "大",
layout: "布局",
layoutListModeTakeEffect: '布局為"列表"模式下生效。',
layoutTileModeTakeEffect: '布局為"平鋪"模式下生效。',
left: "左側",
lineNumber: "行數",
list: "列表",
lock: "鎖定",
lockClassification: "鎖定分類",
lockItem: "鎖定項目",
lockSize: "鎖定尺寸",
mainColor: "主色",
matchCondition: "匹配條件",
medium: "中",
middleClick: "中鍵單擊",
millisecond: "毫秒",
mode: "模式",
modifyIcon: "修改圖標",
mouseHover: "鼠標懸停",
mouseWheel: "鼠標滾輪",
moveTo: "移動到",
multiItems: "多項目",
name: "名稱",
network: "網絡",
networkIcon: "網絡圖標",
networkIconPrompt1: "支持JPG/JPEG/GIF/PNG/ICO/SVG/WEBP格式圖片。",
networkShareCenter: "網絡和共享中心",
newClassification: "新建分類",
newClassificationName: "新分類",
newItem: "新建項目",
newSubclassification: "新建子分類",
none: "無",
noRepeat: "不重復",
normal: "普通",
notDisturb: "勿擾模式",
notDisturbPrompt:
"開啟勿擾模式後計算機在遊戲、應用全屏模式下不會彈出Dawn Launcher窗口。",
notFoundFile: "找不到指定的文件",
notFoundFolder: "找不到指定的文件夾",
number: "數量",
numberKey: "數字鍵",
officialWebsite: "官方網站",
ok: "確定",
open: "打開",
openAfterHideMainInterface: "打開後隱藏主界面",
openAfterHideQuickSearchWindow: "打開後隱藏窗口",
openFileLocation: "打開文件所在位置",
openNow: "僅剩一項時立即打開",
openNumber: "打開次數",
parameters: "參數",
password: "密碼",
pasteIcon: "粘貼圖標",
powerOptions: "電源選項",
programsFeatures: "程序和功能",
proxy: "代理",
proxyPrompt:
"僅支持HTTP代理填寫「地址」時需要帶通信協議和端口例如http://127.0.0.1:7890如果沒有用戶名和密碼為空即可。",
quickSearch: "快速搜索",
recordOpenNumber: "記錄打開次數",
recycleBin: "回收站",
refreshIcon: "刷新圖標",
registryEditor: "註冊表編輯器",
remark: "備註",
rememberSelectionState: "記住選擇狀態",
repeat: "重復",
resourceMonitor: "資源監視器",
restart: "重啟",
restore: "還原",
restoreDataPrompt: "還原數據失敗。",
rewardSponsor: "打賞&贊助",
right: "右側",
roundedCorners: "圓角",
runAsAdministrator: "以管理員身份運行",
runSystemStartup: "開機啟動",
save: "保存",
search: "搜索",
searchSource: "搜索源",
secondaryColor: "副色",
select: "選擇",
selectAll: "全選",
selectItem: "選擇項目",
services: "服務",
setClassificationIcon: "設置分類圖標",
setIcon: "設置圖標",
settings: "設置",
shortcutKey: "快捷鍵",
shortcutKeyPrompt1: "快捷鍵不完整,請完善快捷鍵。",
shortcutKeyPrompt2: (value: string) => {
return '與"' + value + '"分類快捷鍵產生沖突,請重新設置。';
},
shortcutKeyPrompt3: (value: string) => {
return '與"' + value + '"項目快捷鍵產生沖突,請重新設置。';
},
shortcutKeyPrompt4:
'與"設置-常規"中的"顯示/隱藏"快捷鍵產生沖突,請重新設置。',
shortcutKeyPrompt5: '與"設置-常規"中的"搜索"快捷鍵產生沖突,請重新設置。',
shortcutKeyPrompt6:
'與"設置-快速搜索"中的"顯示/隱藏"快捷鍵產生沖突,請重新設置。',
show: "顯示",
showFollowMousePosition: "顯示時跟隨鼠標位置",
showHide: "顯示/隱藏",
showOnlyFiles: "只顯示文件",
showOnlyFolders: "只顯示文件夾",
shutdown: "關機",
simplifiedChinese: "簡體中文",
size: "尺寸",
sleep: "睡眠",
small: "小",
sort: "排序",
startLocation: "起始位置",
startMenu: "開始菜單",
startup: "啟動",
startupTray: "啟動後最小化到系統托盤",
subclassification: "子分類",
svgIcon: "SVG圖標",
svgIconPrompt1: "輸入SVG代碼後需要先點擊「校驗代碼」按鈕。",
switch: "切換",
switchClassificationCollapseOtherSubClassification:
"切換分類時收起其他子分類",
switchEnglish: "顯示窗口時將輸入法切換為英文模式",
system: "系統",
target: "目標",
taskManager: "任務管理器",
theme: "主題",
timeInterval: "時間間隔",
tile: "平鋪",
title: "標題",
top: "頂部",
traditionalChinese: "繁體中文",
turnOffMonitor: "關閉顯示器",
unlockClassification: "解鎖分類",
unlockItem: "解鎖項目",
uploadIcon: "上傳圖標",
url: "網址",
useItemOpen: "從程序外拖動文件到項目圖標上時用此項目打開文件",
useProxy: "使用代理",
useQuickSearch: "使用快速搜索",
username: "用戶名",
update: "更新",
webSearch: "網絡搜索",
webSearchModePrompt1:
'輸入"冒號 + 關鍵字 + 空格"或"關鍵字 + 空格"使用網絡搜索,例如使用谷歌搜索,輸入":g"或"g",然後按下空格鍵,進入網絡搜索模式。',
webSearchModePrompt2: "{w}為搜索關鍵字。",
width: "寬度",
window: "窗口",
zoom: "縮放",
};
// 英语
let english = {
about: "About",
add: "Add",
address: "Address",
aggregateClassification: "Aggregate Classification",
aggregateClassificationPrompt1:
"Aggregation classification can aggregate items under all categories together, sort and display them according to the set sorting method and number of items.",
aggregateClassificationPrompt2:
"The current classification is the parent classification, and aggregate classification cannot be set.",
aggregateClassificationPrompt3:
"The current settings are sorted by the number of opens. You need to enable Settings-Item-Open Count the number of opens.",
aggregateClassificationPrompt4:
"Setting an aggregation classification will clear all items under the original classification. Please operate with caution. Are you sure?",
align: "Align",
all: "All",
altNumberKey: "Alt + Number Key",
alwaysCenter: "Always Center",
alwaysTop: "Always Top",
appearance: "Appearance",
appx: "Appx",
associateFolder: "Associate Folder",
associateFolderPrompt1:
"Associate folder can monitor changes in the specified folder in real time and synchronize them to the corresponding classification.",
associateFolderPrompt2:
"Name of the file/folder to be hidden, multiple separated by commas",
associateFolderPrompt3:
"The current classification is the parent classification and associate folder cannot be set.",
associateFolderPrompt4: "The target path does not exist.",
associateFolderPrompt5: "The destination path must be a folder.",
associateFolderPrompt6:
"Setting the associate folder will clear all items under the original classification. Please operate with caution. Are you sure?",
associateFolderPrompt7:
"The target path is empty. The current classification will be automatically converted to a normal classification. Are you sure?",
autoSwitchClassification: "Auto Switch Classification on Scroll",
backgroundIcon: "Background Icon",
backgroundImage: "Background Image",
backgroundImageMode: "Background Image Mode",
backgroundImagePostion: "Background Image Postion",
backgroundImageTransparent: "Background Image Transparent",
backgroundTransparent: "Background Transparent",
backup: "Backup",
backupRestoreData: "Backup/Restore Data",
backupRestoreDataPrompt:
"If you want to restore the backup data of versions 1.2.3 and earlier, please choose to import a file in JSON format when restoring.",
batchConvertAbsolutePath: "Batch Convert to Absolute Path",
batchConvertRelativePath: "Batch Convert to Relative Path",
batchCopyTo: "Batch Copy to",
batchDelete: "Batch Delete",
batchDeletePrompt: "Delete items in batches?",
batchMoveTo: "Batch Move to",
batchOperation: "Batch Operation",
batchRefreshIcon: "Batch Refresh Icon",
borderColor: "Border Color",
bottom: "Bottom",
byInitialLetter: "by Initial Letter",
byLastOpen: "by Last Open",
byOpenNumber: "by Open Count",
calculator: "Calculator",
cancel: "Cancel",
cancelBatchOperation: "Cancel Batch Operation",
center: "Center",
checkCode: "Check Code",
checkInvalidItem: "Check for Invalid Items",
checkUpdates: "Check Updates",
checkUpdatesPrompt1:
'Check that there is a new version, click "OK" to jump to the official website to download the latest version.',
checkUpdatesPrompt2: "It is currently the latest version.",
checkUpdatesPrompt3:
"Failed to check for updates, please confirm the network.",
classification: "Classification",
clear: "Clear",
close: "Close",
colon: ": ",
colonKeywordSpace: "Colon + Keyword + Space",
color: "Color",
columnNumber: "Column Number",
commandPrompt: "Command Prompt",
computer: "Computer",
computerManagement: "Computer Management",
controlPanel: "Control Panel",
convertAbsolutePath: "Convert to Absolute Path",
convertRelativePath: "Convert to Relative Path",
copyFullPath: "Copy Full Path",
copyTo: "Copy to",
createShortcut: "Create Shortcut",
ctrlNumberKey: "Ctrl + Number Key",
default: "Default",
defaultIcon: "Default Icon",
delayDisplay: "Delay Display",
delayHide: "Delay Hide",
delete: "Delete",
deleteClassificationPrompt: "Delete current classification?",
deleteIcon: "Delete Icon",
deleteItemPrompt: "Delete current item?",
description: "Description",
display: "Display",
displayMainWindow: "Display Main Interface",
documents: "Documents",
doubleClickOpen: "Double Click Open",
doubleClickTaskbar: "Double Click Taskbar",
downloadImagePrompt1: "Failed to download image.",
downloadImagePrompt2: "The link is not in image format.",
dontPromptAgain: "Don't prompt again",
edgeAutoHide: "Auto Hide at Desktop Edge",
edgeDock: "Edge Dock",
edit: "Edit",
editClassification: "Edit Classification",
editItem: "Edit Item",
editSubclassification: "Edit Subclassification",
emptyRecycleBin: "Empty Recycle Bin",
enable: "Enable",
english: "English",
excludeSearch: "Exclude Search",
exit: "Exit",
explorerMenu: "Explorer Menu",
exportIcon: "Export Icon",
extraLarge: "Extra Large",
feedback: "Feedback",
file: "File",
fileExplorer: "File Explorer",
fixedClassification: "Fixed Classification",
fixedIcon: "Fixed Icon",
fixedPosition: "Fixed Position",
folder: "Folder",
folderPath: "Folder Path",
fontLineHeight: "Font Line Height",
fontMainColor: "Font Main Color",
fontSecondaryColor: "Font Secondary Color",
fontShadow: "Font Shadow",
fontSize: "Font Size",
fontWeight: "Font Weight",
functions: "Functions",
general: "General",
getIcon: "Get Icon",
getURLInformation: "Get URL Information",
globalShortcutKey: "Global Shortcut Key",
hiddenItems: "Hidden Items",
hideEllipses: "Hide Ellipses",
hideLoseFocus: "Hide After Lose Focus",
hideName: "Hide Name",
hideTray: "Hide Tray",
hideWindowCollapseSubClassification:
"Collapse Subclassification when Hide Window",
history: "History",
hover: "Hover",
icon: "Icon",
imageLink: "Image Link",
item: "Item",
itemAddEditPrompt1:
"After selecting the fixed icon, the current project will not participate in refreshing the icon.",
itemAddEditPrompt2: "Leave it blank if there are no special requirements",
itemAddEditPrompt3: "Add successfully.",
itemAddEditPrompt4: "The target does not exist.",
itemAddEditPrompt5: "Failed to obtain URL information.",
itemList: "Item List",
keyword: "Keyword",
keywordSpace: "Leyword + Space",
language: "Language",
large: "Large",
layout: "Layout",
layoutListModeTakeEffect: 'Effective when the layout is in "list" mode.',
layoutTileModeTakeEffect: 'Effective when the layout is in "tile" mode.',
left: "Left",
lineNumber: "Number of Lines",
list: "List",
lock: "Lock",
lockClassification: "Lock Classification",
lockItem: "Lock Item",
lockSize: "Lock Size",
mainColor: "Main Color",
matchCondition: "Match Condition",
medium: "Medium",
middleClick: "Middle Click",
millisecond: "Millisecond",
mode: "Mode",
modifyIcon: "Modify Icon",
mouseHover: "Mouse Hover",
mouseWheel: "Mouse Wheel",
moveTo: "Move to",
multiItems: "Multi Items",
name: "Name",
network: "Network",
networkIcon: "Network Icon",
networkIconPrompt1: "Supports JPG/JPEG/GIF/PNG/ICO/SVG/WEBP format images.",
networkShareCenter: "Network Share Center",
newClassification: "New Classification",
newClassificationName: "New Classification",
newItem: "New Item",
newSubclassification: "New Subclassification",
none: "None",
noRepeat: "No Repeat",
normal: "Normal",
notDisturb: "Not Disturb Mode",
notDisturbPrompt:
"After turning on Do Not Disturb mode, the Dawn Launcher window will not pop up when the computer is in full-screen mode for games or applications.",
notFoundFile: "The file specified cannot be found",
notFoundFolder: "The specified folder cannot be found",
number: "Number",
numberKey: "Number Key",
officialWebsite: "Official Website",
ok: "OK",
open: "Open",
openAfterHideMainInterface: "Hide Main Interface After Open",
openAfterHideQuickSearchWindow: "Hide Window After Open",
openFileLocation: "Open File Location",
openNow: "Open when Only One Item",
openNumber: "Open Count",
parameters: "Parameters",
password: "Password",
pasteIcon: "Paste Icon",
powerOptions: "Power Options",
programsFeatures: "Programs Features",
proxy: "Proxy",
proxyPrompt:
'Only HTTP proxy is supported. When filling in the "address", you need to include the communication protocol and port, for example: http://127.0.0.1:7890. If there is no username and password, just leave it blank.',
quickSearch: "Quick Search",
recordOpenNumber: "Record Open Count",
recycleBin: "Recycle Bin",
refreshIcon: "Refresh Icon",
registryEditor: "Registry Editor",
remark: "Remark",
rememberSelectionState: "Remember Selection State",
repeat: "Repeat",
resourceMonitor: "Resource Monitor",
restart: "Restart",
restore: "Restore",
restoreDataPrompt: "Failed to restore data.",
rewardSponsor: "Reward&Sponsor",
right: "Right",
roundedCorners: "Rounded Corners",
runAsAdministrator: "Run as Administrator",
runSystemStartup: "Run System Startup",
save: "Save",
search: "Search",
searchSource: "Search Source",
secondaryColor: "Secondary Color",
select: "Select",
selectAll: "Select All",
selectItem: "Select Item",
services: "Services",
setClassificationIcon: "Set Classification Icon",
setIcon: "Set Icon",
settings: "Settings",
shortcutKey: "Shortcut Key",
shortcutKeyPrompt1:
"The shortcut keys are incomplete, please improve the shortcut keys.",
shortcutKeyPrompt2: (value: string) => {
return (
"There is a conflict with the " +
value +
" classification shortcut keys, please reset."
);
},
shortcutKeyPrompt3: (value: string) => {
return (
"There is a conflict with the " +
value +
" item shortcut keys, please reset."
);
},
shortcutKeyPrompt4:
'It conflicts with the "Show/Hide" shortcut key in "Settings-General", please reset it.',
shortcutKeyPrompt5:
'It conflicts with the "Search" shortcut key in "Settings-General", please reset it.',
shortcutKeyPrompt6:
'It conflicts with the "Show/Hide" shortcut key in "Settings-Quick Search", please reset it.',
show: "Show",
showFollowMousePosition: "Follows Mouse Position on Display",
showHide: "Show/Hide",
showOnlyFiles: "Show Only Files",
showOnlyFolders: "Show Only Folders",
shutdown: "Shutdown关机",
simplifiedChinese: "Simplified Chinese",
size: "Size",
sleep: "Sleep",
small: "Small",
sort: "Sort",
startLocation: "Start Location",
startMenu: "Start Menu",
startup: "Startup",
startupTray: "Minimize to System Tray on Startup",
subclassification: "Subclassification",
svgIcon: "SVG Icon",
svgIconPrompt1:
'After entering the SVG code, you need to click the "Check Code" button first.',
switch: "Switch",
switchClassificationCollapseOtherSubClassification:
"Collapse Other Subclassification on Classification Switch",
switchEnglish: "Switch to English Input on Window Display",
system: "System",
target: "Target",
taskManager: "Task Manager",
theme: "Theme",
timeInterval: "Time Interval",
tile: "Tile",
title: "Title",
top: "Top",
traditionalChinese: "Traditional Chinese",
turnOffMonitor: "Turn Off Monitor",
unlockClassification: "Unlock Classification",
unlockItem: "Unlock Item",
uploadIcon: "Upload Icon",
url: "URL",
useItemOpen: "Use This Item to Open Dragged Files",
useProxy: "Use Proxy",
useQuickSearch: "Use Quick Search",
username: "Username",
update: "Update",
webSearch: "Web Search",
webSearchModePrompt1:
'Enter "Colon + Keyword + Space" or "Keyword + Space" to use a web search, such as using Google search, enter ":g" or "g", and then press the space bar to enter web search mode.',
webSearchModePrompt2: "{w} is the search keyword.",
width: "Width",
window: "Window",
zoom: "Zoom",
};
/**
*
* @param language
* @returns
*/
function getLanguage(language: string | null) {
if (language === "SimplifiedChinese") {
return simplifiedChinese;
} else if (language === "TraditionalChinese") {
return traditionalChinese;
} else if (language === "English") {
return english;
}
return simplifiedChinese;
}
export { getLanguage };

4
commons/data/theme.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
// 无用为了tsc编译通过
import { Theme } from "../../types/setting";
export const themeList: Array<Theme>;

86
commons/data/theme.ts Normal file
View File

@ -0,0 +1,86 @@
import { Theme } from "../../types/setting";
const themeList: Array<Theme> = [
{
name: "#FFFFFF",
mainFontColor: "#505050FF",
secondFontColor: "#505050FF",
mainBackgroundColor: "#FFFFFFFF",
secondBackgroundColor: "#DCDEDFFF",
borderColor: "#F0F0F0FF",
},
{
name: "#2B2B2B",
mainFontColor: "#BBBBBBFF",
secondFontColor: "#BBBBBBFF",
mainBackgroundColor: "#2B2B2BFF",
secondBackgroundColor: "#3C3F41FF",
borderColor: "#3C3F41FF",
},
{
name: "#508CC8",
mainFontColor: "#FFFFFFFF",
secondFontColor: "#FFFFFFFF",
mainBackgroundColor: "#508CC8FF",
secondBackgroundColor: "#6FA0D2FF",
borderColor: "#6FA0D2FF",
},
{
name: "#024351",
mainFontColor: "#FFFFFFFF",
secondFontColor: "#FFFFFFFF",
mainBackgroundColor: "#024351FF",
secondBackgroundColor: "#025A6CFF",
borderColor: "#025A6CFF",
},
{
name: "#516FA3",
mainFontColor: "#FFFFFFFF",
secondFontColor: "#FFFFFFFF",
mainBackgroundColor: "#516FA3FF",
secondBackgroundColor: "#91A8D0FF",
borderColor: "#91A8D0FF",
},
{
name: "#45326E",
mainFontColor: "#FFFFFFFF",
secondFontColor: "#FFFFFFFF",
mainBackgroundColor: "#45326EFF",
secondBackgroundColor: "#5F4B8BFF",
borderColor: "#5F4B8BFF",
},
{
name: "#693030",
mainFontColor: "#FFFFFFFF",
secondFontColor: "#FFFFFFFF",
mainBackgroundColor: "#693030FF",
secondBackgroundColor: "#955151FF",
borderColor: "#803A3AFF",
},
{
name: "#9F2F4A",
mainFontColor: "#FFFFFFFF",
secondFontColor: "#FFFFFFFF",
mainBackgroundColor: "#9F2F4AFF",
secondBackgroundColor: "#EA6F8CFF",
borderColor: "#AD3350FF",
},
{
name: "#000000,#FFDB00",
mainFontColor: "#B3B3B3FF",
secondFontColor: "#000000FF",
mainBackgroundColor: "#000000FF",
secondBackgroundColor: "#FFDB00FF",
borderColor: "#1D1D1DFF",
},
{
name: "#000000,#FFFFFF",
mainFontColor: "#B3B3B3FF",
secondFontColor: "#000000FF",
mainBackgroundColor: "#000000FF",
secondBackgroundColor: "#FFFFFFFF",
borderColor: "#1D1D1DFF",
},
];
export { themeList };

4
commons/data/webSearchSource.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
// 无用为了tsc编译通过
import { WebSearchSource } from "../../types/setting";
export const webSearchSourceList: Array<WebSearchSource>;

View File

@ -0,0 +1,41 @@
import { WebSearchSource } from "../../types/setting";
const webSearchSourceList: Array<WebSearchSource> = [
{
id: 1,
keyword: "g",
name: "Google",
url: "https://www.google.com/search?q={w}",
description: null,
},
{
id: 2,
keyword: "b",
name: "Baidu",
url: "https://www.baidu.com/s?wd={w}",
description: null,
},
{
id: 3,
keyword: "bing",
name: "Bing",
url: "https://cn.bing.com/search?q={w}",
description: null,
},
{
id: 4,
keyword: "so",
name: "360",
url: "https://www.so.com/s?q={w}",
description: null,
},
{
id: 5,
keyword: "sogou",
name: "Sogou",
url: "https://www.sogou.com/web?query={w}",
description: null,
},
];
export { webSearchSourceList };

71
commons/utils/common.d.ts vendored Normal file
View File

@ -0,0 +1,71 @@
// 无用为了tsc编译通过
import { Classification, ClassificationData } from "../../types/classification";
import { CommonItem, CommonItemData, Item, ItemData } from "../../types/item";
export function convert<F, T>(from: F): T;
export function isAbsolutePath(path: string): boolean;
export function deleteExtname(name: string | null): string | null;
export function getFileName(path: string | null): string | null;
export function getFileExtname(path: string | null): string | null;
export function newClassification(data: {
id?: number | null;
parentId?: number | null;
name?: string | null;
type?: number | null;
data?: ClassificationData | null;
shortcutKey?: string | null;
globalShortcutKey?: boolean | null;
order?: number | null;
childList?: Array<Classification> | null;
}): Classification;
export function newClassificationData(data: {
icon?: string | null;
associateFolderPath?: string | null;
associateFolderHiddenItems?: string | null;
itemLayout?: "default" | "tile" | "list";
itemSort?: "default" | "initial" | "openNumber" | "lastOpen";
itemColumnNumber?: number | null;
itemIconSize?: number | null;
itemShowOnly?: "default" | "file" | "folder";
fixed?: boolean | null;
aggregateItemCount?: number | null;
}): ClassificationData;
export function newCommonItem(data: {
id?: number | null;
name?: string | null;
data?: CommonItemData | null;
order?: number | null;
}): CommonItem;
export function newCommonItemData(data: {
target?: string | null;
params?: string | null;
icon?: string | null;
htmlIcon?: string | null;
}): CommonItemData;
export function newItem(data: {
id?: number | null;
classificationId: number;
name?: string | null;
type?: number | null;
data?: ItemData | null;
shortcutKey?: string | null;
globalShortcutKey?: boolean | null;
order?: number | null;
}): Item;
export function newItemData(data: {
startLocation?: string | null;
target?: string | null;
params?: string | null;
runAsAdmin?: boolean | null;
icon?: string | null;
htmlIcon?: string | null;
remark?: string | null;
iconBackgroundColor?: boolean | null;
fixedIcon?: boolean | null;
openNumber?: number | null;
lastOpen?: number | null;
quickSearchOpenNumber?: number | null;
quickSearchLastOpen?: number | null;
multiItemsTimeInterval?: number | null;
}): ItemData;
export function getItemName(name: string | null): string;

308
commons/utils/common.ts Normal file
View File

@ -0,0 +1,308 @@
import { Classification, ClassificationData } from "../../types/classification";
import { CommonItem, CommonItemData, Item, ItemData } from "../../types/item";
/**
*
*/
function convert<F, T>(from: F): T {
return JSON.parse(JSON.stringify(from)) as T;
}
/**
*
* @param path
* @returns
*/
function isAbsolutePath(path: string) {
const regex = /^[a-zA-Z]:\\/;
return regex.test(path);
}
/**
*
* @param name
* @returns
*/
function deleteExtname(name: string | null) {
if (name && name.trim() !== "") {
if (name.indexOf(".") > 0) {
return name.substring(0, name.lastIndexOf("."));
} else {
return name;
}
}
return null;
}
/**
*
* @param path
* @returns
*/
function getFileName(path: string | null) {
if (path && path.trim() !== "") {
let split = path.split("\\");
return split[split.length - 1];
} else {
return null;
}
}
/**
*
* @param path
* @returns
*/
function getFileExtname(path: string | null) {
// 获取文件名
let fileName = getFileName(path);
if (fileName && fileName.trim() !== "") {
if (fileName.indexOf(".") >= 0) {
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
}
}
return null;
}
/**
* CLassification
* @returns
*/
function newClassification({
id = null,
parentId = null,
name = null,
type = null,
data = null,
shortcutKey = null,
globalShortcutKey = false,
order = null,
childList = null,
}: {
id?: number | null;
parentId?: number | null;
name?: string | null;
type?: number | null;
data?: ClassificationData | null;
shortcutKey?: string | null;
globalShortcutKey?: boolean | null;
order?: number | null;
childList?: Array<Classification> | null;
}): Classification {
return {
id: id ?? 0,
parentId: parentId ?? null,
name: name ?? null,
type: type ?? 0,
data: data ? newClassificationData(data) : newClassificationData({}),
shortcutKey: shortcutKey ?? null,
globalShortcutKey: globalShortcutKey ?? false,
order: order ?? 0,
childList: childList ?? null,
};
}
/**
* ClassificationData
* @returns
*/
function newClassificationData({
icon = null,
associateFolderPath = null,
associateFolderHiddenItems = null,
itemLayout = "default",
itemSort = "default",
itemColumnNumber = null,
itemIconSize = null,
itemShowOnly = "default",
fixed = false,
aggregateItemCount = 50,
excludeSearch = false,
}: {
icon?: string | null;
associateFolderPath?: string | null;
associateFolderHiddenItems?: string | null;
itemLayout?: "default" | "tile" | "list";
itemSort?: "default" | "initial" | "openNumber" | "lastOpen";
itemColumnNumber?: number | null;
itemIconSize?: number | null;
itemShowOnly?: "default" | "file" | "folder";
fixed?: boolean | null;
aggregateItemCount?: number | null;
excludeSearch?: boolean | null;
}): ClassificationData {
return {
icon: icon ?? null,
associateFolderPath: associateFolderPath ?? null,
associateFolderHiddenItems: associateFolderHiddenItems ?? null,
itemLayout: itemLayout ?? "default",
itemSort: itemSort ?? "default",
itemColumnNumber: itemColumnNumber ?? null,
itemIconSize: itemIconSize ?? null,
itemShowOnly: itemShowOnly ?? "default",
fixed: fixed ?? false,
aggregateItemCount: aggregateItemCount ?? 50,
excludeSearch: excludeSearch ?? false,
};
}
/**
* CommonItem
* @returns
*/
function newCommonItem({
id = null,
name = null,
data = null,
order = null,
}: {
id?: number | null;
name?: string | null;
data?: CommonItemData | null;
order?: number | null;
}): CommonItem {
return {
id: id ?? 0,
name: name ?? null,
data: data ? newCommonItemData(data) : newCommonItemData({}),
order: order ?? 0,
};
}
/**
* CommonItemData
* @returns
*/
function newCommonItemData({
target = null,
params = null,
icon = null,
htmlIcon = null,
}: {
target?: string | null;
params?: string | null;
icon?: string | null;
htmlIcon?: string | null;
}): CommonItemData {
return {
target: target ?? null,
params: params ?? null,
icon: icon ?? null,
htmlIcon: htmlIcon ?? null,
};
}
/**
* Item
* @returns
*/
function newItem({
id = null,
classificationId,
name = null,
type = null,
data = null,
shortcutKey = null,
globalShortcutKey = false,
order = null,
}: {
id?: number | null;
classificationId: number;
name?: string | null;
type?: number | null;
data?: ItemData | null;
shortcutKey?: string | null;
globalShortcutKey?: boolean | null;
order?: number | null;
}): Item {
return {
id: id ?? 0,
classificationId,
name: name ?? null,
type: type ?? 0,
data: data ? newItemData(data) : newItemData({}),
shortcutKey: shortcutKey ?? null,
globalShortcutKey: globalShortcutKey ?? false,
order: order ?? 0,
};
}
/**
* ItemData
* @returns
*/
function newItemData({
startLocation = null,
target = null,
params = null,
runAsAdmin = false,
icon = null,
htmlIcon = null,
remark = null,
iconBackgroundColor = false,
fixedIcon = false,
openNumber = 0,
lastOpen = 0,
quickSearchOpenNumber = 0,
quickSearchLastOpen = 0,
multiItemsTimeInterval = 0,
}: {
startLocation?: string | null;
target?: string | null;
params?: string | null;
runAsAdmin?: boolean | null;
icon?: string | null;
htmlIcon?: string | null;
remark?: string | null;
iconBackgroundColor?: boolean | null;
fixedIcon?: boolean | null;
openNumber?: number | null;
lastOpen?: number | null;
quickSearchOpenNumber?: number | null;
quickSearchLastOpen?: number | null;
multiItemsTimeInterval?: number | null;
}): ItemData {
return {
startLocation: startLocation ?? null,
target: target ?? null,
params: params ?? null,
runAsAdmin: runAsAdmin ?? false,
icon: icon ?? null,
htmlIcon: htmlIcon ?? null,
remark: remark ?? null,
iconBackgroundColor: iconBackgroundColor ?? false,
fixedIcon: fixedIcon ?? false,
openNumber: openNumber ?? 0,
lastOpen: lastOpen ?? 0,
quickSearchOpenNumber: quickSearchOpenNumber ?? 0,
quickSearchLastOpen: quickSearchLastOpen ?? 0,
multiItemsTimeInterval: multiItemsTimeInterval ?? 0,
};
}
/**
*
* @param name
* @returns
*/
function getItemName(name: string | null) {
if (name) {
return name.replace(/\\n/g, " ");
}
return "";
}
export {
convert,
isAbsolutePath,
deleteExtname,
getFileName,
getFileExtname,
newClassification,
newClassificationData,
newCommonItem,
newCommonItemData,
newItem,
newItemData,
getItemName,
};

4
commons/utils/setting.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
// 无用为了tsc编译通过
import { Setting } from "../../types/setting";
export function getSetting(setting: Setting | null): Setting;

378
commons/utils/setting.ts Normal file
View File

@ -0,0 +1,378 @@
import {
Appearance,
Classification,
General,
Item,
Network,
Proxy,
QuickSearch,
Setting,
SubClassification,
Theme,
WebSearch,
WebSearchSource,
} from "../../types/setting";
import { convert } from "./common";
import { themeList } from "../data/theme";
import { webSearchSourceList } from "../data/webSearchSource";
/**
*
* @returns
*/
function getGeneral({
startup = false,
startupTray = false,
showHideShortcutKey = null,
language = "SimplifiedChinese",
alwaysTop = false,
edgeAdsorb = true,
edgeAutoHide = false,
lockSize = false,
hideLoseFocus = false,
hideTray = false,
fixedPosition = false,
alwaysCenter = false,
showHideMouseWheelClick = false,
showFollowMousePosition = false,
notDisturb = false,
showHideDoubleClickTaskbar = false,
delayDisplayMs = 0,
delayHideMs = 0,
switchEnglish = false,
searchShowHideShortcutKey = "TAB",
checkUpdates = true,
}: {
startup?: boolean | null;
startupTray?: boolean | null;
showHideShortcutKey?: string | null;
language?: string | null;
alwaysTop?: boolean | null;
edgeAdsorb?: boolean | null;
edgeAutoHide?: boolean | null;
lockSize?: boolean | null;
hideLoseFocus?: boolean | null;
hideTray?: boolean | null;
fixedPosition?: boolean | null;
alwaysCenter?: boolean | null;
showHideMouseWheelClick?: boolean | null;
showFollowMousePosition?: boolean | null;
notDisturb?: boolean | null;
showHideDoubleClickTaskbar?: boolean | null;
delayDisplayMs?: number | null;
delayHideMs?: number | null;
switchEnglish?: boolean | null;
searchShowHideShortcutKey?: string | null;
checkUpdates?: boolean | null;
}): General {
return {
startup: startup ?? false,
startupTray: startupTray ?? false,
showHideShortcutKey: showHideShortcutKey ?? null,
language: language ?? "SimplifiedChinese",
alwaysTop: alwaysTop ?? false,
edgeAdsorb: edgeAdsorb ?? true,
edgeAutoHide: edgeAutoHide ?? false,
lockSize: lockSize ?? false,
hideLoseFocus: hideLoseFocus ?? false,
hideTray: hideTray ?? false,
fixedPosition: fixedPosition ?? false,
alwaysCenter: alwaysCenter ?? false,
showHideMouseWheelClick: showHideMouseWheelClick ?? false,
showFollowMousePosition: showFollowMousePosition ?? false,
notDisturb: notDisturb ?? false,
showHideDoubleClickTaskbar: showHideDoubleClickTaskbar ?? false,
delayDisplayMs: delayDisplayMs ?? 0,
delayHideMs: delayHideMs ?? 0,
switchEnglish: switchEnglish ?? false,
searchShowHideShortcutKey: searchShowHideShortcutKey ?? null,
checkUpdates: checkUpdates ?? true,
};
}
/**
*
* @returns
*/
function getAppearance({
theme = convert<Theme, Theme>(themeList[0]),
transparency = 1,
backgroundImage = null,
backgroundImageTransparency = 1,
backgroundImageMode = "repeat",
backgroundImagePosition = "default",
fontShadow = false,
fontShadowColor = "#000000",
windowRounded = false,
title = "Dawn Launcher",
}: {
theme?: Theme | null;
transparency?: number | null;
backgroundImage?: string | null;
backgroundImageTransparency?: number | null;
backgroundImageMode?: string | null;
backgroundImagePosition?: string | null;
fontShadow?: boolean | null;
fontShadowColor?: string | null;
windowRounded?: boolean | null;
title?: string | null;
}): Appearance {
return {
theme: theme ?? convert<Theme, Theme>(themeList[0]),
transparency: transparency ?? 1,
backgroundImage: backgroundImage ?? null,
backgroundImageTransparency: backgroundImageTransparency ?? 1,
backgroundImageMode: backgroundImageMode ?? "repeat",
backgroundImagePosition: backgroundImagePosition ?? "default",
fontShadow: fontShadow ?? false,
fontShadowColor: fontShadowColor ?? "#000000",
windowRounded: windowRounded ?? false,
title: title ?? "Dawn Launcher",
};
}
/**
*
* @returns
*/
function getClassification({
width = 140,
layout = "left",
mouseHover = false,
mouseHoverMs = 0,
mouseWheel = false,
rememberSelectionState = false,
nameAlign = "left",
mode = "normal",
autoSwitchClassification = false,
hideWindowCollapseSubClassification = false,
switchClassificationCollapseOtherSubClassification = false,
}: {
width?: number | null;
layout?: string | null;
mouseHover?: boolean | null;
mouseHoverMs?: number | null;
mouseWheel?: boolean | null;
rememberSelectionState?: boolean | null;
nameAlign?: string | null;
mode?: string | null;
autoSwitchClassification?: boolean | null;
hideWindowCollapseSubClassification?: boolean | null;
switchClassificationCollapseOtherSubClassification?: boolean | null;
}): Classification {
return {
width: width ?? 140,
layout: layout ?? "left",
mouseHover: mouseHover ?? false,
mouseHoverMs: mouseHoverMs ?? 0,
mouseWheel: mouseWheel ?? false,
rememberSelectionState: rememberSelectionState ?? false,
nameAlign: nameAlign ?? "left",
mode: mode ?? "normal",
autoSwitchClassification: autoSwitchClassification ?? false,
hideWindowCollapseSubClassification:
hideWindowCollapseSubClassification ?? false,
switchClassificationCollapseOtherSubClassification:
switchClassificationCollapseOtherSubClassification ?? false,
};
}
/**
*
* @returns
*/
function getSubClassification({
itemAreaNameFontSize = 14,
itemAreaNameFontWeight = 700,
itemAreaNameFontLineHeight = 1.25,
}: {
itemAreaNameFontSize?: number | null;
itemAreaNameFontWeight?: number | null;
itemAreaNameFontLineHeight?: number | null;
}): SubClassification {
return {
itemAreaNameFontSize: itemAreaNameFontSize ?? 14,
itemAreaNameFontWeight: itemAreaNameFontWeight ?? 700,
itemAreaNameFontLineHeight: itemAreaNameFontLineHeight ?? 1.25,
};
}
/**
*
* @returns
*/
function getItem({
layout = "tile",
iconSize = 40,
doubleClickOpen = false,
openAfterHideMainInterface = false,
useItemOpen = false,
openNumber = false,
hideItemName = false,
hideEllipsis = false,
itemNameRowCount = 2,
width = 85,
columnNumber = 1,
checkInvalidItem = false,
fontSize = 14,
fontWeight = 400,
fontLineHeight = 1.25,
}: {
layout?: string | null;
iconSize?: number | null;
doubleClickOpen?: boolean | null;
openAfterHideMainInterface?: boolean | null;
useItemOpen?: boolean | null;
openNumber?: boolean | null;
hideItemName?: boolean | null;
hideEllipsis?: boolean | null;
itemNameRowCount?: number | null;
width?: number | null;
columnNumber?: number | null;
checkInvalidItem?: boolean | null;
fontSize?: number | null;
fontWeight?: number | null;
fontLineHeight?: number | null;
}): Item {
return {
layout: layout ?? "tile",
iconSize: iconSize ?? 40,
doubleClickOpen: doubleClickOpen ?? false,
openAfterHideMainInterface: openAfterHideMainInterface ?? false,
useItemOpen: useItemOpen ?? false,
openNumber: openNumber ?? false,
hideItemName: hideItemName ?? false,
hideEllipsis: hideEllipsis ?? false,
itemNameRowCount: itemNameRowCount ?? 2,
width: width ?? 85,
columnNumber: columnNumber ?? 1,
checkInvalidItem: checkInvalidItem ?? false,
fontSize: fontSize ?? 14,
fontWeight: fontWeight ?? 400,
fontLineHeight: fontLineHeight ?? 1.25,
};
}
/**
*
* @returns
*/
function getQuickSearch({
enable = true,
showHideShortcutKey = "Alt + Space",
openShortcutKey = "none",
hideLoseFocus = false,
openNow = false,
showHistory = false,
showHistorySort = "lastOpen",
useItemOpen = false,
openAfterHideQuickSearchWindow = true,
matchConditionsRemark = false,
}: {
enable?: boolean | null;
showHideShortcutKey?: string | null;
openShortcutKey?: string | null;
hideLoseFocus?: boolean | null;
openNow?: boolean | null;
showHistory?: boolean | null;
showHistorySort?: string | null;
useItemOpen?: boolean | null;
openAfterHideQuickSearchWindow?: boolean | null;
matchConditionsRemark?: boolean | null;
}): QuickSearch {
return {
enable: enable ?? true,
showHideShortcutKey: showHideShortcutKey ?? "Alt + Space",
openShortcutKey: openShortcutKey ?? "none",
hideLoseFocus: hideLoseFocus ?? false,
openNow: openNow ?? false,
showHistory: showHistory ?? false,
showHistorySort: showHistorySort ?? "lastOpen",
useItemOpen: useItemOpen ?? false,
openAfterHideQuickSearchWindow: openAfterHideQuickSearchWindow ?? true,
matchConditionsRemark: matchConditionsRemark ?? false,
};
}
/**
*
* @returns
*/
function getWebSearch({
mode = 0,
searchSourceList = webSearchSourceList,
}: {
mode?: number | null;
searchSourceList?: Array<WebSearchSource> | null;
}): WebSearch {
return {
mode: mode ?? 0,
searchSourceList: searchSourceList ?? webSearchSourceList,
};
}
/**
*
* @returns
*/
function getNetwork({
useProxy = false,
proxy = getProxy({}),
}: {
useProxy?: boolean | null;
proxy?: Proxy | null;
}): Network {
return {
useProxy: useProxy ?? false,
proxy: proxy ? getProxy(proxy) : getProxy({}),
};
}
/**
*
* @returns
*/
function getProxy({
address = "",
username = null,
password = null,
}: {
address?: string | null;
username?: string | null;
password?: string | null;
}): Proxy {
return {
address: address ?? "",
username: username ?? null,
password: password ?? null,
};
}
/**
*
* @returns
*/
function getSetting(setting: Setting | null): Setting {
return {
general: getGeneral(setting && setting.general ? setting.general : {}),
appearance: getAppearance(
setting && setting.appearance ? setting.appearance : {}
),
classification: getClassification(
setting && setting.classification ? setting.classification : {}
),
subClassification: getSubClassification(
setting && setting.subClassification ? setting.subClassification : {}
),
item: getItem(setting && setting.item ? setting.item : {}),
quickSearch: getQuickSearch(
setting && setting.quickSearch ? setting.quickSearch : {}
),
webSearch: getWebSearch(
setting && setting.webSearch ? setting.webSearch : {}
),
network: getNetwork(setting && setting.network ? setting.network : {}),
};
}
export { getSetting };

35
electron-builder.json5 Normal file
View File

@ -0,0 +1,35 @@
/**
* @see https://www.electron.build/configuration/configuration
*/
{
$schema: "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json",
appId: "com.dawnlauncher.application",
asar: true,
compression: "maximum",
productName: "Dawn Launcher",
directories: {
output: "release/${version}",
},
asarUnpack: ["**/*.node"],
npmRebuild: false,
files: ["dist", "dist-electron", "native", "!node_modules/**/*"],
win: {
appId: "com.dawnlauncher.application",
target: [
{
target: "nsis",
arch: ["x64"],
},
],
artifactName: "${productName}-Windows-${version}-Setup.${ext}",
icon: "public/logo.ico",
},
nsis: {
artifactName: "${productName}-${version}.${ext}",
oneClick: false,
allowElevation: true,
allowToChangeInstallationDirectory: true,
createDesktopShortcut: true,
createStartMenuShortcut: true,
},
}

View File

@ -0,0 +1,40 @@
import { join, dirname } from "node:path";
import Database from "better-sqlite3-multiple-ciphers";
import { getUserDataPath } from "../main/commons";
let database: Database.Database;
let cacheDatabase: Database.Database;
function getDataSqlite3() {
let filename = join(getUserDataPath(), "Data.db");
database ??= new Database(filename, {
nativeBinding: join(
process.env.NODE_ENV !== "development" ? dirname(process.execPath) : "",
import.meta.env.VITE_BETTER_SQLITE3_BINDING
),
});
return database;
}
function getCacheDataSqlite3() {
let filename = join(getUserDataPath(), "CacheData.db");
cacheDatabase ??= new Database(filename, {
nativeBinding: join(
process.env.NODE_ENV !== "development" ? dirname(process.execPath) : "",
import.meta.env.VITE_BETTER_SQLITE3_BINDING
),
});
return cacheDatabase;
}
function getCustomDataSqlite3(filePath: string) {
let db = new Database(filePath, {
nativeBinding: join(
process.env.NODE_ENV !== "development" ? dirname(process.execPath) : "",
import.meta.env.VITE_BETTER_SQLITE3_BINDING
),
});
return db;
}
export { getDataSqlite3, getCacheDataSqlite3, getCustomDataSqlite3 };

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,32 @@
import { app } from "electron";
import Logger from "electron-log";
import { join, dirname } from "node:path";
// 名称
let date = new Date();
let logName =
date.getFullYear() +
"-" +
(date.getMonth() + 1 < 10
? "0" + (date.getMonth() + 1)
: date.getMonth() + 1) +
"-" +
date.getDate();
// 日志
if (
process.env.NODE_ENV !== "development" &&
import.meta.env.VITE_INSTALL === "false"
) {
Logger.transports.file.resolvePathFn = () =>
join(dirname(process.execPath), "data", "logs", logName + ".log");
} else {
Logger.transports.file.resolvePathFn = () =>
join(app.getPath("userData"), "logs", logName + ".log");
}
export default {
error(content: any) {
Logger.error(content);
},
};

View File

@ -0,0 +1,80 @@
import { MessageChannelMain, app, utilityProcess } from "electron";
import { join } from "node:path";
import { writeFileSync, readFileSync, unlink } from "node:fs";
import { ChildProcessInfo } from "../types/global";
/**
*
* @param name
* @param data
*/
function fork(name: string, data: any, callback: Function) {
// 随机数
let min = 1;
let max = 99999;
let random = Math.floor(Math.random() * (max - min + 1)) + min;
// 子进程
const { port1, port2 } = new MessageChannelMain();
const childProcess = utilityProcess.fork(join(__dirname, "worker.js"));
// 存储子进程信息
if (!global.childProcessMap) {
global.childProcessMap = new Map();
}
global.childProcessMap.set(childProcess.pid, <ChildProcessInfo>{
utilityProcess: childProcess,
port1,
port2,
});
// 获取临时目录
let temp = app.getPath("temp");
// 参数文件
let paramFilePath =
temp + "\\" + random + "." + new Date().getTime() + "." + name + ".txt";
// 创建文件并写入数据
writeFileSync(paramFilePath, JSON.stringify(data), {
encoding: "utf-8",
});
// 发送消息
let params = {
name,
data: {
filePath: paramFilePath,
},
};
// 创建子进程完成后发送消息
childProcess.once("spawn", () => {
childProcess.postMessage(JSON.stringify(params), [port1]);
});
// 等待接收消息
port2.start();
port2.once("message", (event) => {
let data: string = event.data;
try {
if (data !== "exit") {
// 读取文件
let res = readFileSync(data, { encoding: "utf-8" });
// 删除文件
unlink(data, () => {});
// 回调
callback(JSON.parse(res));
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
} finally {
// 关闭子进程
childProcess.kill();
}
});
// 监听关闭子进程
childProcess.once("exit", () => {
// 关闭通道
port1.close();
port2.close();
// 删除信息
global.childProcessMap.delete(childProcess.pid);
});
}
export { fork };

155
electron/commons/utils.ts Normal file
View File

@ -0,0 +1,155 @@
import { resolve, dirname, parse } from "node:path";
import { isAbsolutePath } from "../../commons/utils/common";
import mime from "mime";
import { readFileSync } from "node:fs";
// 图标格式
const iconExts = ["jpg", "jpeg", "png", "gif", "ico", "svg", "webp"];
/**
* user-agent
*/
function getRandomUserAgent() {
const userAgents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/89.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.59",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/89.0",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 OPR/76.0.4017.123",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 OPR/76.0.4017.123",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/89.0",
];
const randomIndex = Math.floor(Math.random() * userAgents.length);
return userAgents[randomIndex];
}
/**
*
* @param paramsMap
* @returns
*/
function getURLParams(paramsMap: Map<string, any>) {
let res = "";
if (paramsMap.size > 0) {
res += "?";
let params: string | null = null;
paramsMap.forEach((value, key) => {
if (params) {
params += "&" + key + "=" + value;
} else {
params = key + "=" + value;
}
});
res += params;
}
return res;
}
/**
*
* @param path
*/
function parseEnvPath(path: string) {
// 尝试解析路径中的环境变量
let parsedPath = parse(path);
let isBase = false;
let pathArr: Array<string> = [];
if (!parsedPath.dir || parsedPath.dir.trim() === "") {
pathArr = parsedPath.base.split("\\");
isBase = true;
} else {
pathArr = parsedPath.dir.split("\\");
}
// 新路径
let newPathArr: Array<string> = [];
// 正则提取环境变量 %{path}% 提取中间的path名称
const pattern = /^%.*%$/;
for (let string of pathArr) {
// 符合环境变量正则
if (pattern.test(string)) {
// 尝试获取
let env: string | null = global.addon.getEnvByName(
string.substring(1, string.length - 1)
);
// 如果提取到环境变量了就使用环境变量路径,如果没有就使用原路径
if (env && env.trim() !== "") {
newPathArr.push(env);
} else {
newPathArr.push(string);
}
} else {
// 没有匹配到正则,使用原路径
newPathArr.push(string);
}
}
// 根据上方解析如果拥有dir的话需要追加base变量
if (!isBase) {
newPathArr.push(parsedPath.base);
}
// 拼接并返回
return newPathArr.join("\\");
}
/**
*
* @param path
*/
function getAbsolutePath(path: string) {
if (!isAbsolutePath(path)) {
// 尝试解析环境变量
let newPath = parseEnvPath(path);
// 判断解析之后的路径是否是绝对路径
if (isAbsolutePath(newPath)) {
return newPath;
} else {
return resolve(
process.env.NODE_ENV === "development"
? resolve(".")
: dirname(process.execPath),
path
);
}
}
return path;
}
/**
*
* @param filePath
*/
function getFileIcon(filePath: string | null) {
// 图标
let icon: string | null = null;
if (filePath) {
// 获取后缀
let ext = mime.getExtension(mime.getType(filePath));
if (iconExts.includes(ext)) {
// 读取文件
let buffer = readFileSync(filePath);
icon =
"data:" +
mime.getType(filePath) +
";base64," +
buffer.toString("base64");
} else {
// 获取图标
icon = global.addon.getFileIcon(filePath);
}
}
return icon;
}
export {
getURLParams,
getAbsolutePath,
getFileIcon,
iconExts,
getRandomUserAgent,
};

11
electron/electron-env.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
/// <reference types="vite-plugin-electron/electron-env" />
declare namespace NodeJS {
interface ProcessEnv {
VSCODE_DEBUG?: 'true'
DIST_ELECTRON: string
DIST: string
/** /dist/ or /public/ */
VITE_PUBLIC: string
}
}

View File

@ -0,0 +1,58 @@
import { BrowserWindow, shell } from "electron";
import { closeWindow, getMainBackgorunColor } from "../commons";
import { join } from "node:path";
// 窗口
let aboutWindow: BrowserWindow | null = null;
/**
*
*/
function createWindow() {
// 如果窗口存在先关闭窗口
closeWindow(aboutWindow);
// 创建窗口
aboutWindow = global.aboutWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 212,
width: 600,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
aboutWindow.loadURL(process.env.VITE_DEV_SERVER_URL + "About");
} else {
aboutWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/About",
});
}
aboutWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
aboutWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
aboutWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
aboutWindow.setEnabled(true);
}, 100);
return true;
});
}
export { createWindow };

View File

@ -0,0 +1,20 @@
import { ipcMain } from "electron";
import { createWindow } from ".";
import { closeWindow } from "../commons";
export default function () {
// 创建窗口
ipcMain.on("createAboutWindow", (event, args) => {
createWindow();
});
// 显示窗口
ipcMain.on("showAboutWindow", (event, args) => {
if (global.aboutWindow) {
global.aboutWindow.show();
}
});
// 关闭窗口
ipcMain.on("closeAboutWindow", (event, args) => {
closeWindow(global.aboutWindow);
});
}

View File

@ -0,0 +1,397 @@
import { deleteAssociateFolderWatcher } from ".";
import {
Classification,
ClassificationData,
} from "../../../types/classification";
import {
newClassification,
newClassificationData,
} from "../../../commons/utils/common";
import { deleteByClassificationId, updateClassificationId } from "../item/data";
import { getDataSqlite3 } from "../../commons/betterSqlite3";
// 获取数据库
let db = getDataSqlite3();
// 分类表名
let classificationTableName = "classification";
// 查询字段
let selectColumn =
"id, parent_id parentId, name, type, data, shortcut_key shortcutKey, global_shortcut_key globalShortcutKey, `order`";
/**
*
*/
function getClassification(row: any): Classification {
return newClassification({
id: row.id,
parentId: row.parentId,
name: row.name,
type: row.type,
data: newClassificationData(JSON.parse(row.data)),
shortcutKey: row.shortcutKey,
globalShortcutKey: row.globalShortcutKey === 1,
order: row.order,
});
}
/**
*
*/
function init() {
// sql
let sql = `CREATE TABLE IF NOT EXISTS ${classificationTableName} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
parent_id INTEGER,
name TEXT NOT NULL,
type INTEGER NOT NULL,
data TEXT NOT NULL,
shortcut_key TEXT,
global_shortcut_key INTEGER NOT NULL,
\`order\` INTEGER NOT NULL)`;
// 运行
db.exec(sql);
// 查询有多少条数据
sql = `SELECT COUNT(id) count FROM ${classificationTableName}`;
let row: any = db.prepare(sql).get();
let count = row.count as number;
if (count === 0) {
// 新增分类
add(null, global.language.newClassificationName, null, false);
}
}
/**
*
* @param parentId
*/
function list(parentId: number | null = null) {
// 参数
let params = [];
// sql
let sql = `SELECT ${selectColumn} FROM ${classificationTableName}`;
if (parentId) {
sql += " WHERE parent_id = ?";
params.push(parentId);
}
sql += " ORDER BY `order` ASC";
// 查询
let list = db.prepare(sql).all(params);
// 返回
return list.map((row) => {
return getClassification(row);
});
}
/**
*
* @param parentId
* @param name
* @param shortcutKey
* @param globalShortcutKey
* @returns
*/
function add(
parentId: number | null,
name: string,
shortcutKey: string | null,
globalShortcutKey: boolean,
data: ClassificationData = newClassificationData({}),
type: number = 0
): Classification | null {
// 获取序号
let newOrder = getMaxOrder(parentId) + 1;
// SQL
let sql = `INSERT INTO ${classificationTableName} (parent_id, name, type, data, shortcut_key, global_shortcut_key, \`order\`) VALUES (?, ?, ?, ?, ?, ?, ?)`;
// 运行
let id = db
.prepare(sql)
.run(
parentId,
name,
type,
JSON.stringify(data),
shortcutKey,
globalShortcutKey ? 1 : 0,
newOrder
).lastInsertRowid as number;
if (id) {
let classification = newClassification({
id,
name,
parentId,
type,
data,
shortcutKey,
globalShortcutKey,
order: newOrder,
});
// 如果是添加子分类,将父级分类下的项目移动到新建的子分类中
if (parentId) {
updateClassificationId(parentId, id);
}
return classification;
}
return null;
}
/**
*
* @param id
* @param name
* @param shortcutKey
* @param globalShortcutKey
* @returns
*/
function update(classification: Classification) {
// SQL
let sql = `UPDATE ${classificationTableName} SET name = ?, type = ?, data = ?, shortcut_key = ?, global_shortcut_key = ? WHERE id = ?`;
// 运行
return (
db
.prepare(sql)
.run(
classification.name,
classification.type,
JSON.stringify(classification.data),
classification.shortcutKey,
classification.globalShortcutKey ? 1 : 0,
classification.id
).changes > 0
);
}
/**
*
* @param id
* @param data
*/
function updateData(id: number, data: ClassificationData) {
// SQL
let sql = `UPDATE ${classificationTableName} SET data = ? WHERE id = ?`;
return db.prepare(sql).run(JSON.stringify(data), id).changes > 0;
}
/**
* ID查询
* @param id
*/
function selectById(id: number): Classification | null {
// SQL
let sql = `SELECT ${selectColumn} FROM ${classificationTableName} WHERE id = ?`;
// 运行
let row = db.prepare(sql).get(id);
// 返回
if (row) {
return getClassification(row);
} else {
return null;
}
}
/**
*
* @param id
*/
function del(id: number) {
// 查询数据
let classifictaion = selectById(id);
if (classifictaion) {
// 查询有无子分类
let childList = list(classifictaion.id);
// SQL
let sql = `DELETE FROM ${classificationTableName} WHERE id = ? or parent_id = ?`;
// 运行
let res = db.prepare(sql).run(id, id).changes > 0;
if (res) {
// 更新序号
reorder(classifictaion.parentId);
// 删除分类下所有项目
deleteByClassificationId(id);
// 删除子分类下所有项目
for (const child of childList) {
deleteByClassificationId(child.id);
if (child.type === 1) {
// 删除关联文件夹
deleteAssociateFolderWatcher(child.id);
}
}
if (classifictaion.type === 1) {
// 删除关联文件夹
deleteAssociateFolderWatcher(classifictaion.id);
}
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
*
* @param fromId
* @param toId
* @param parentId
*/
function updateOrder(
fromId: number,
toId: number | null,
parentId: number | null
) {
// 查询来源分类
let fromClassification = selectById(fromId);
if (fromClassification) {
// 新序号
let newOrder = 1;
// 如果目标ID不为空获取项目并获取序号
if (toId) {
let toClassification = selectById(toId);
if (toClassification) {
newOrder = toClassification.order;
}
} else {
newOrder = getMaxOrder(parentId) + 1;
}
// SQL
let sql = `UPDATE ${classificationTableName} SET \`order\` = ? WHERE id = ?`;
// 更新排序
db.prepare(sql).run(newOrder, fromClassification.id);
// 判断新序号和老序号之间的数据是+1还是-1
if (newOrder > fromClassification.order) {
// 新序号和老序号之间数据,序号-1
let params = [fromClassification.order, newOrder, fromClassification.id];
sql = `UPDATE ${classificationTableName} SET \`order\` = \`order\` - 1 WHERE \`order\` > ? AND \`order\` <= ? AND id != ?`;
if (parentId) {
sql += " AND parent_id = ?";
params.push(parentId);
} else {
sql += " AND parent_id is NULL";
}
db.prepare(sql).run(params);
} else {
// 新序号和老序号之间数据,序号+1
let params = [newOrder, fromClassification.order, fromClassification.id];
sql = `UPDATE ${classificationTableName} SET \`order\` = \`order\` + 1 WHERE \`order\` >= ? AND \`order\` < ? AND id != ?`;
if (parentId) {
sql += " AND parent_id = ?";
params.push(parentId);
} else {
sql += " AND parent_id is NULL";
}
db.prepare(sql).run(params);
}
return true;
}
return false;
}
/**
*
* @param parentId
*/
function reorder(parentId: number | null) {
// 查询分类列表
let classificationList = list(parentId);
// 开启事务
db.transaction(() => {
// SQL
let sql = `UPDATE ${classificationTableName} SET \`order\` = ? WHERE id = ?`;
// 更新序号
for (let i = 0; i < classificationList.length; i++) {
db.prepare(sql).run(i + 1, classificationList[i].id);
}
})();
}
/**
*
* @param parentId
*/
function getMaxOrder(parentId: number | null) {
// SQL
let sql = `SELECT MAX(\`order\`) \`order\` FROM ${classificationTableName}`;
if (parentId) {
sql += " WHERE parent_id = ?";
} else {
sql += " WHERE parent_id IS NULL";
}
// 运行
let row: any = db.prepare(sql).get(parentId ? [parentId] : []);
if (!row || !row.order) {
return 0;
} else {
return row.order;
}
}
/**
*
* @param id
* @param icon
*/
function updateIcon(id: number, icon: string | null) {
// 查询分类
let classification = selectById(id);
if (classification) {
// SQL
let sql = `UPDATE ${classificationTableName} SET data = ? WHERE id = ?`;
// 更新图标
classification.data.icon = icon;
return (
db.prepare(sql).run(JSON.stringify(classification.data), id).changes > 0
);
}
return false;
}
/**
*
* @param id
*/
function hasChildClassification(id: number) {
let classificationList = list(id);
return classificationList.length > 0;
}
/**
*
* @param classification
* @param fixed
*/
function updateFixed(classification: Classification, fixed: boolean) {
classification.data.fixed = fixed;
updateData(classification.id, classification.data);
}
/**
*
* @param id
*/
function batchUpdateFixed(id: number | null = null) {
// 事务
db.transaction(() => {
// 查询所有分类
let classificationList = list();
// 更新
for (const classification of classificationList) {
updateFixed(classification, id === classification.id);
}
})();
}
export {
init,
list,
add,
del,
selectById,
update,
updateData,
updateOrder,
updateIcon,
hasChildClassification,
batchUpdateFixed,
};

View File

@ -0,0 +1,954 @@
import { BrowserWindow, MenuItem, shell } from "electron";
import { join, basename } from "node:path";
import { getURLParams } from "../../commons/utils";
import {
add,
hasChildClassification,
list,
selectById,
update,
updateData,
} from "./data";
import { watch, statSync, Stats, readdirSync } from "node:fs";
import { getDirectoryList } from "../item";
import { AssociateFolderData } from "../../types/global";
import { Classification } from "../../../types/classification";
import {
closeWindow,
getDot,
getMainBackgorunColor,
sendToWebContent,
} from "../commons/index";
import { deleteByClassificationId } from "../item/data";
// 窗口
let classificationAddEditWindow: BrowserWindow | null = null;
let classificationSetIconWindow: BrowserWindow | null = null;
let classificationAssociateFolderWindow: BrowserWindow | null = null;
let classificationAggregateWindow: BrowserWindow | null = null;
/**
* /
* @param id
* @param parentId
*/
function createAddEditWindow(id: number | null, parentId: number | null) {
// 如果窗口存在先关闭窗口
closeWindow(classificationAddEditWindow);
// 创建窗口
classificationAddEditWindow = global.classificationAddEditWindow =
new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 174,
width: 400,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
// 参数
let params = new Map();
if (id) {
params.set("id", id);
}
if (parentId) {
params.set("parentId", parentId);
}
if (process.env.VITE_DEV_SERVER_URL) {
classificationAddEditWindow.loadURL(
process.env.VITE_DEV_SERVER_URL +
"Classification/AddEdit" +
getURLParams(params)
);
} else {
classificationAddEditWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/Classification/AddEdit",
search: getURLParams(params),
});
}
classificationAddEditWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
classificationAddEditWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
classificationAddEditWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
classificationAddEditWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
* @param id
*/
function createSetIconWindow(id: number) {
// 如果窗口存在先关闭窗口
closeWindow(classificationSetIconWindow);
// 创建窗口
classificationSetIconWindow = global.classificationSetIconWindow =
new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 500,
width: 358,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
// 参数
let params = new Map();
params.set("id", id);
if (process.env.VITE_DEV_SERVER_URL) {
classificationSetIconWindow.loadURL(
process.env.VITE_DEV_SERVER_URL +
"Classification/SetIcon" +
getURLParams(params)
);
} else {
classificationSetIconWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/Classification/SetIcon",
search: getURLParams(params),
});
}
classificationSetIconWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
classificationSetIconWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
classificationSetIconWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
classificationSetIconWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
* @param id
*/
function createAssociateFolderWindow(id: number) {
// 如果窗口存在先关闭窗口
closeWindow(classificationAssociateFolderWindow);
// 创建窗口
classificationAssociateFolderWindow =
global.classificationAssociateFolderWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 249,
width: 400,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
// 参数
let params = new Map();
params.set("id", id);
if (process.env.VITE_DEV_SERVER_URL) {
classificationAssociateFolderWindow.loadURL(
process.env.VITE_DEV_SERVER_URL +
"Classification/AssociateFolder" +
getURLParams(params)
);
} else {
classificationAssociateFolderWindow.loadFile(
join(process.env.DIST, "index.html"),
{
hash: "/Classification/AssociateFolder",
search: getURLParams(params),
}
);
}
classificationAssociateFolderWindow.webContents.setWindowOpenHandler(
({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
}
);
// 禁用标题栏右键
classificationAssociateFolderWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
classificationAssociateFolderWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
classificationAssociateFolderWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
* @param id
*/
function createAggregateWindow(id: number) {
// 如果窗口存在先关闭窗口
closeWindow(classificationAggregateWindow);
// 创建窗口
classificationAggregateWindow = global.classificationAggregateWindow =
new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 144,
width: 400,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
// 参数
let params = new Map();
params.set("id", id);
if (process.env.VITE_DEV_SERVER_URL) {
classificationAggregateWindow.loadURL(
process.env.VITE_DEV_SERVER_URL +
"Classification/Aggregate" +
getURLParams(params)
);
} else {
classificationAggregateWindow.loadFile(
join(process.env.DIST, "index.html"),
{
hash: "/Classification/Aggregate",
search: getURLParams(params),
}
);
}
classificationAggregateWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
classificationAggregateWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
classificationAggregateWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
classificationAggregateWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
*/
function addAssociateFolderWatcher(
classificationId: number,
dir: string,
hiddenItems: string | null
) {
// 先删除原有监听
deleteAssociateFolderWatcher(classificationId);
// 新建监听
let data: AssociateFolderData = {
classificationId,
dir,
hiddenItems,
watch: null,
};
// 定时器
let timer: NodeJS.Timeout | null = null;
let dirWatch = watch(dir, () => {
if (timer) {
clearTimeout(timer);
timer = null;
}
// 启动定时器,在指定的时间间隔后发送合并后的通知
timer = setTimeout(() => {
getDirectoryList(classificationId, dir, hiddenItems, false, true);
clearTimeout(timer);
timer = null;
}, 2000);
});
dirWatch.on("error", () => {
dirWatch.close();
global.associateFolderWatcher.delete(classificationId);
});
// 保存
data.watch = dirWatch;
if (!global.associateFolderWatcher) {
global.associateFolderWatcher = new Map();
}
global.associateFolderWatcher.set(classificationId, data);
}
/**
*
*/
function deleteAssociateFolderWatcher(classificationId: number) {
if (global.associateFolderWatcher) {
if (global.associateFolderWatcher.has(classificationId)) {
let data = global.associateFolderWatcher.get(classificationId);
if (data.watch) {
data.watch.close();
data.watch = null;
}
global.associateFolderWatcher.delete(classificationId);
}
} else {
global.associateFolderWatcher = new Map();
}
}
/**
*
* @param id
* @param dir
* @param hiddenItems
*/
function setAssociateFolder(
id: number,
dir: string | null,
hiddenItems: string | null
) {
// 查询分类
let classification = selectById(id);
if (classification) {
// 类型
if (!dir) {
classification.type = 0;
classification.data.associateFolderPath = null;
} else {
classification.type = 1;
classification.data.associateFolderPath = dir;
}
classification.data.associateFolderHiddenItems = hiddenItems;
// 更新
let res = update(classification);
if (res) {
// 如果类型为1开始读取文件夹的文件
if (classification.type === 1) {
// 读取文件夹并创建监听
getDirectoryList(classification.id, dir, hiddenItems, true, true);
} else {
// 删除监听
deleteAssociateFolderWatcher(classification.id);
}
return classification;
}
}
return null;
}
/**
*
*/
function initAssociateFolder() {
// 初始化Map
global.associateFolderWatcher = new Map();
// 查询分类
let classificationList = list(null);
// 初始化
for (const classification of classificationList) {
if (classification.type === 1) {
// 读取文件夹并创建监听
getDirectoryList(
classification.id,
classification.data.associateFolderPath,
classification.data.associateFolderHiddenItems,
true,
true
);
}
}
}
/**
*
* @param classification
* @returns
*/
function getItemLayoutMenu(classification: Classification) {
// 是否拥有子级
let hasChild = hasChildClassification(classification.id);
// 菜单
let submenus: any = [
new MenuItem({
label: global.language.default,
icon:
classification.data.itemLayout === "default" && !hasChild
? getDot()
: null,
click: () => {
updateItemLayout(classification, "default");
},
}),
new MenuItem({
label: global.language.tile,
icon:
classification.data.itemLayout === "tile" && !hasChild
? getDot()
: null,
click: () => {
updateItemLayout(classification, "tile");
},
}),
new MenuItem({
label: global.language.list,
icon:
classification.data.itemLayout === "list" && !hasChild
? getDot()
: null,
click: () => {
updateItemLayout(classification, "list");
},
}),
];
return new MenuItem({
label: global.language.layout,
submenu: submenus,
});
}
/**
*
* @param classification
* @param layout
*/
function updateItemLayout(
classification: Classification,
layout: "default" | "tile" | "list"
) {
let resultList = [];
// 尝试获取子级分类
let childList = list(classification.id);
let idList = [];
// 如果有子级分类的话,连同子级分类一起修改
if (childList && childList.length > 0) {
idList.push(...childList.map((c) => c.id));
}
idList.push(classification.id);
// 修改
for (const id of idList) {
// 查询分类
let classification = selectById(id);
if (classification) {
classification.data.itemLayout = layout;
if (updateData(classification.id, classification.data)) {
resultList.push({
id,
layout,
});
}
}
}
// 通知页面
sendToWebContent("mainWindow", "onUpdateItemLayout", resultList);
}
/**
*
* @param classification
* @returns
*/
function getItemSortMenu(classification: Classification) {
// 是否拥有子级
let hasChild = hasChildClassification(classification.id);
// 菜单
let submenus: any = [
new MenuItem({
label: global.language.default,
icon:
classification.data.itemSort === "default" && !hasChild
? getDot()
: null,
click: () => {
updateItemSort(classification, "default");
},
}),
new MenuItem({
label: global.language.byInitialLetter,
icon:
classification.data.itemSort === "initial" && !hasChild
? getDot()
: null,
click: () => {
updateItemSort(classification, "initial");
},
}),
new MenuItem({
label: global.language.byLastOpen,
icon:
classification.data.itemSort === "lastOpen" && !hasChild
? getDot()
: null,
click: () => {
updateItemSort(classification, "lastOpen");
},
}),
];
if (global.setting.item.openNumber) {
submenus.push(
new MenuItem({
label: global.language.byOpenNumber,
icon:
classification.data.itemSort === "openNumber" && !hasChild
? getDot()
: null,
click: () => {
updateItemSort(classification, "openNumber");
},
})
);
}
return new MenuItem({
label: global.language.sort,
submenu: submenus,
});
}
/**
*
* @param classification
* @param sort
*/
function updateItemSort(
classification: Classification,
sort: "default" | "initial" | "openNumber" | "lastOpen"
) {
let resultList = [];
// 尝试获取子级分类
let childList = list(classification.id);
let idList = [];
// 如果有子级分类的话,连同子级分类一起修改
if (childList && childList.length > 0) {
idList.push(...childList.map((c) => c.id));
}
idList.push(classification.id);
// 修改
for (const id of idList) {
// 查询分类
let classification = selectById(id);
if (classification && classification.type !== 2) {
classification.data.itemSort = sort;
if (updateData(classification.id, classification.data)) {
resultList.push({
id,
sort,
});
}
}
}
// 通知页面
sendToWebContent("mainWindow", "onUpdateItemSort", resultList);
}
/**
*
* @param classification
* @returns
*/
function getItemColumnNumber(classification: Classification) {
// 菜单
let submenus: any = [
new MenuItem({
label: global.language.default,
icon: !classification.data.itemColumnNumber ? getDot() : null,
click: () => {
updateItemColumnNumber(classification, null);
},
}),
];
for (let i = 0; i < 20; i++) {
submenus.push({
label: (i + 1).toString(),
icon:
classification.data.itemColumnNumber &&
classification.data.itemColumnNumber === i + 1
? getDot()
: null,
click: () => {
updateItemColumnNumber(classification, i + 1);
},
});
}
return new MenuItem({
label: global.language.columnNumber,
submenu: submenus,
});
}
/**
*
* @param classification
* @param columnNumber
*/
function updateItemColumnNumber(
classification: Classification,
columnNumber: number | null
) {
// 查询分类
let curClassification = selectById(classification.id);
if (curClassification) {
curClassification.data.itemColumnNumber = columnNumber;
if (updateData(curClassification.id, curClassification.data)) {
// 通知页面
sendToWebContent("mainWindow", "onUpdateItemColumnNumber", {
id: curClassification.id,
columnNumber: columnNumber,
});
}
}
}
/**
*
* @param classification
* @returns
*/
function getItemIconSize(classification: Classification) {
// 是否拥有子级
let hasChild = hasChildClassification(classification.id);
// 菜单
let submenus: any = [
new MenuItem({
label: global.language.default,
icon: !classification.data.itemIconSize && !hasChild ? getDot() : null,
click: () => {
updateItemIconSize(classification, null);
},
}),
new MenuItem({
label: global.language.extraLarge,
icon:
classification.data.itemIconSize === 48 && !hasChild ? getDot() : null,
click: () => {
updateItemIconSize(classification, 48);
},
}),
new MenuItem({
label: global.language.large,
icon:
classification.data.itemIconSize === 40 && !hasChild ? getDot() : null,
click: () => {
updateItemIconSize(classification, 40);
},
}),
new MenuItem({
label: global.language.medium,
icon:
classification.data.itemIconSize === 32 && !hasChild ? getDot() : null,
click: () => {
updateItemIconSize(classification, 32);
},
}),
new MenuItem({
label: global.language.small,
icon:
classification.data.itemIconSize === 24 && !hasChild ? getDot() : null,
click: () => {
updateItemIconSize(classification, 24);
},
}),
];
return new MenuItem({
label: global.language.icon,
submenu: submenus,
});
}
/**
*
* @param classification
* @param iconSize
*/
function updateItemIconSize(
classification: Classification,
iconSize: number | null
) {
let resultList = [];
// 尝试获取子级分类
let childList = list(classification.id);
let idList = [];
// 如果有子级分类的话,连同子级分类一起修改
if (childList && childList.length > 0) {
idList.push(...childList.map((c) => c.id));
}
idList.push(classification.id);
// 修改
for (const id of idList) {
// 查询分类
let classification = selectById(id);
if (classification) {
classification.data.itemIconSize = iconSize;
if (updateData(classification.id, classification.data)) {
resultList.push({
id,
iconSize,
});
}
}
}
// 通知页面
sendToWebContent("mainWindow", "onUpdateItemIconSize", resultList);
}
/**
*
* @param pathList
*/
function addClassificationByDirectory(pathList: Array<string>) {
// 返回信息
let resultList = [];
// 循环每个路径
for (let path of pathList) {
try {
// 获取文件类型
let stats: Stats | null = null;
try {
stats = statSync(path);
} catch (e) {
path = path.replace(" (x86)", "");
try {
stats = statSync(path);
} catch (e) {}
}
// 只要文件夹
if (stats && stats.isDirectory()) {
// 文件夹名称
let name = basename(path);
// 添加分类
let classification = add(null, name, null, false);
if (classification) {
// 读取文件夹下的内容
let files = readdirSync(path);
// 组装路径
let fileList = [];
for (let file of files) {
fileList.push(join(path, file));
}
// 返回信息
resultList.push({
classification,
fileList,
});
}
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
}
return resultList;
}
/**
*
* @param classification
* @returns
*/
function getItemShowOnly(classification: Classification) {
// 是否拥有子级
let hasChild = hasChildClassification(classification.id);
// 菜单
let submenus: any = [
new MenuItem({
label: global.language.all,
icon:
classification.data.itemShowOnly === "default" && !hasChild
? getDot()
: null,
click: () => {
updateItemShowOnly(classification, "default");
},
}),
new MenuItem({
label: global.language.showOnlyFiles,
icon:
classification.data.itemShowOnly === "file" && !hasChild
? getDot()
: null,
click: () => {
updateItemShowOnly(classification, "file");
},
}),
new MenuItem({
label: global.language.showOnlyFolders,
icon:
classification.data.itemShowOnly === "folder" && !hasChild
? getDot()
: null,
click: () => {
updateItemShowOnly(classification, "folder");
},
}),
];
return new MenuItem({
label: global.language.show,
submenu: submenus,
});
}
/**
*
* @param classification
* @param iconSize
*/
function updateItemShowOnly(
classification: Classification,
showOnly: "default" | "file" | "folder"
) {
let resultList = [];
// 尝试获取子级分类
let childList = list(classification.id);
let idList = [];
// 如果有子级分类的话,连同子级分类一起修改
if (childList && childList.length > 0) {
idList.push(...childList.map((c) => c.id));
}
idList.push(classification.id);
// 修改
for (const id of idList) {
// 查询分类
let classification = selectById(id);
if (classification) {
classification.data.itemShowOnly = showOnly;
if (updateData(classification.id, classification.data)) {
resultList.push({
id,
showOnly,
});
}
}
}
// 通知页面
sendToWebContent("mainWindow", "onUpdateItemShowOnly", resultList);
}
/**
*
*/
function updateItemOpenNumberSortToDefualt() {
let resultList = [];
// 查询分类
let classificationList = list();
// 筛选出来排序是打开次数的分类
let filterList = classificationList.filter(
(c) => c.data.itemSort === "openNumber" && (c.type === 0 || c.type === 1)
);
// 修改
for (const classification of filterList) {
classification.data.itemSort = "default";
if (updateData(classification.id, classification.data)) {
resultList.push(classification.id);
}
}
// 通知页面
sendToWebContent(
"mainWindow",
"onUpdateItemOpenNumberSortToDefualt",
resultList
);
}
/**
*
* @param id
* @param sort
* @param itemCount
*/
function updateAggregate(
id: number,
sort: "default" | "initial" | "openNumber" | "lastOpen",
itemCount: number
) {
let classification = selectById(id);
if (classification) {
classification.type = 2;
classification.data.itemSort = sort;
classification.data.aggregateItemCount = itemCount;
let res = update(classification);
if (res) {
deleteByClassificationId(id);
}
return res;
}
return false;
}
/**
*
* @param id
* @param value
*/
function updateExcludeSearch(id: number, value: boolean) {
// 查询分类
let classification = selectById(id);
if (classification) {
classification.data.excludeSearch = value;
updateData(id, classification.data);
}
}
export {
createAddEditWindow,
createSetIconWindow,
createAssociateFolderWindow,
createAggregateWindow,
setAssociateFolder,
addAssociateFolderWatcher,
initAssociateFolder,
deleteAssociateFolderWatcher,
getItemSortMenu,
getItemLayoutMenu,
getItemColumnNumber,
getItemIconSize,
addClassificationByDirectory,
getItemShowOnly,
updateItemOpenNumberSortToDefualt,
updateAggregate,
updateExcludeSearch,
};

View File

@ -0,0 +1,336 @@
import { Menu, MenuItem, ipcMain, dialog } from "electron";
import { Classification } from "../../../types/classification";
import {
createAddEditWindow,
createAssociateFolderWindow,
createSetIconWindow,
getItemLayoutMenu,
getItemSortMenu,
getItemColumnNumber,
setAssociateFolder,
getItemIconSize,
addClassificationByDirectory,
getItemShowOnly,
createAggregateWindow,
updateAggregate,
updateExcludeSearch,
} from "./index";
import {
add,
del,
list,
selectById,
update,
updateOrder,
updateIcon,
hasChildClassification,
batchUpdateFixed,
} from "./data";
import { setShortcutKey } from "../setting";
import { closeWindow, getDot, sendToWebContent } from "../commons/index";
export default function () {
// 获取分类列表
ipcMain.on("getClassificationList", (event) => {
event.returnValue = list(null);
});
// 根据ID查询分类
ipcMain.on("getClassificationById", (event, args) => {
event.returnValue = selectById(args.id);
});
// 添加分类
ipcMain.on("addClassification", (event, args) => {
let classification = add(
args.parentId,
args.name,
args.shortcutKey,
args.globalShortcutKey
);
setShortcutKey();
event.returnValue = classification;
});
// 更新分类
ipcMain.on("updateClassification", (event, args) => {
let res = update(args);
setShortcutKey();
event.returnValue = res;
});
// 更新序号
ipcMain.on("updateClassificationOrder", (event, args) => {
event.returnValue = updateOrder(args.fromId, args.toId, args.parentId);
});
// 更新图标
ipcMain.on("updateClassificationIcon", (event, args) => {
event.returnValue = updateIcon(args.id, args.icon);
});
// 显示新增/修改窗口
ipcMain.on("showClassificationAddEditWindow", () => {
if (global.classificationAddEditWindow) {
global.classificationAddEditWindow.show();
}
});
// 关闭新增/修改窗口
ipcMain.on("closeClassificationAddEditWindow", () => {
closeWindow(global.classificationAddEditWindow);
});
// 显示设置图标窗口
ipcMain.on("showClassificationSetIconWindow", () => {
if (global.classificationSetIconWindow) {
global.classificationSetIconWindow.show();
}
});
// 关闭设置图标窗口
ipcMain.on("closeClassificationSetIconWindow", () => {
closeWindow(global.classificationSetIconWindow);
});
// 右键菜单
ipcMain.on("showClassificationRightMenu", (event, args) => {
// 锁定/解锁分类
let lockClassification: boolean = args.lockClassification;
// 分类
let classification: Classification | null = args.classification;
// 菜单
let menuList: Array<MenuItem> = [];
// 组装菜单
if (!classification) {
menuList.push(
new MenuItem({
label: global.language.newClassification,
click: () => {
// 创建窗口
createAddEditWindow(null, null);
},
}),
new MenuItem({ type: "separator" }),
new MenuItem({
label: !lockClassification
? global.language.lockClassification
: global.language.unlockClassification,
click: () => {
sendToWebContent("mainWindow", "onLockClassification", []);
},
})
);
} else {
if (!classification.parentId && classification.type === 0) {
menuList.push(
new MenuItem({
label: global.language.newSubclassification,
click: () => {
// 创建窗口
createAddEditWindow(null, classification.id);
},
}),
new MenuItem({ type: "separator" })
);
}
menuList.push(
new MenuItem({
label: global.language.fixedClassification,
icon: classification.data.fixed ? getDot() : null,
click: () => {
batchUpdateFixed(
classification.data.fixed ? null : classification.id
);
sendToWebContent(
"mainWindow",
"onUpdateClassificationFixed",
classification.data.fixed ? null : classification.id
);
},
})
);
if (classification.type === 0 || classification.type === 1) {
menuList.push(
new MenuItem({
label: global.language.excludeSearch,
icon: classification.data.excludeSearch ? getDot() : null,
click: () => {
updateExcludeSearch(
classification.id,
!classification.data.excludeSearch
);
sendToWebContent(
"mainWindow",
"onUpdateClassificationExcludeSearch",
{
id: classification.id,
value: !classification.data.excludeSearch,
}
);
},
})
);
}
menuList.push(new MenuItem({ type: "separator" }));
menuList.push(
new MenuItem({
label: global.language.setIcon,
click: () => {
// 创建窗口
createSetIconWindow(classification.id);
},
}),
new MenuItem({
label: global.language.deleteIcon,
click: () => {
let res = updateIcon(classification.id, null);
if (res) {
sendToWebContent("mainWindow", "onUpdateClassificationIcon", {
id: classification.id,
icon: null,
});
}
},
})
);
// 子分类、没有子分类的父级分类可以显示
if (
classification.parentId ||
(!classification.parentId && !hasChildClassification(classification.id))
) {
menuList.push(new MenuItem({ type: "separator" }));
if (classification.type === 0 || classification.type === 1) {
menuList.push(
new MenuItem({
label: global.language.associateFolder,
click: () => {
// 创建窗口
createAssociateFolderWindow(classification.id);
},
})
);
}
if (classification.type === 0 || classification.type === 2) {
menuList.push(
new MenuItem({
label: global.language.aggregateClassification,
click: () => {
// 创建窗口
createAggregateWindow(classification.id);
},
})
);
}
}
// 分割线
menuList.push(new MenuItem({ type: "separator" }));
if (classification.type !== 2) {
// 排序
menuList.push(getItemSortMenu(classification));
}
// 布局
menuList.push(getItemLayoutMenu(classification));
// 列数
if (
!hasChildClassification(classification.id) &&
(classification.data.itemLayout === "list" ||
(global.setting.item.layout === "list" &&
classification.data.itemLayout === "default"))
) {
// 只有子级分类或没有子级分类的父级分类并且布局是列表的才显示列数
menuList.push(getItemColumnNumber(classification));
}
// 图标
menuList.push(getItemIconSize(classification));
// 显示
menuList.push(getItemShowOnly(classification));
// 编辑/删除
menuList.push(
new MenuItem({ type: "separator" }),
new MenuItem({
label: global.language.edit,
click: () => {
// 创建窗口
createAddEditWindow(classification.id, null);
},
}),
new MenuItem({
label: global.language.delete,
click: () => {
let res = dialog.showMessageBoxSync(global.mainWindow, {
message: global.language.deleteClassificationPrompt,
buttons: [global.language.ok, global.language.cancel],
type: "question",
noLink: true,
cancelId: 1,
});
if (res === 0) {
// 删除数据
if (del(classification.id)) {
// 快捷键
setShortcutKey();
// 通知前端删除数据
sendToWebContent(
"mainWindow",
"onDeleteClassification",
classification.id
);
}
}
},
})
);
}
// 载入菜单
let menu = Menu.buildFromTemplate(menuList);
// 菜单显示
menu.on("menu-will-show", () => {
global.classificationRightMenu = true;
});
// 菜单关闭
menu.on("menu-will-close", () => {
global.classificationRightMenu = false;
});
// 显示
menu.popup();
});
// 显示关联文件夹窗口
ipcMain.on("showClassificationAssociateFolderWindow", () => {
if (global.classificationAssociateFolderWindow) {
global.classificationAssociateFolderWindow.show();
}
});
// 关闭关联文件夹窗口
ipcMain.on("closeClassificationAssociateFolderWindow", () => {
closeWindow(global.classificationAssociateFolderWindow);
});
// 设置关联文件夹
ipcMain.on("setClassificationAssociateFolder", (event, args) => {
// 分类ID
let id: number = args.id;
// 文件夹路径
let dir: string | null = args.dir;
if (!dir || dir.trim() === "") {
dir = null;
}
// 隐藏项
let hiddenItems: string | null = args.hiddenItems;
// 设置
event.returnValue = setAssociateFolder(id, dir, hiddenItems);
});
// 是否拥有子分类
ipcMain.on("hasChildClassification", (event, args) => {
event.returnValue = hasChildClassification(args);
});
// 根据文件夹创建分类
ipcMain.on("addClassificationByDirectory", (event, args) => {
let res = addClassificationByDirectory(args);
// 通知页面
sendToWebContent("mainWindow", "onAddClassificationByDirectory", res);
});
// 显示聚合分类窗口
ipcMain.on("showClassificationAggregateWindow", () => {
if (global.classificationAggregateWindow) {
global.classificationAggregateWindow.show();
}
});
// 关闭聚合分类窗口
ipcMain.on("closeClassificationAggregateWindow", () => {
closeWindow(global.classificationAggregateWindow);
});
// 更新聚合分类
ipcMain.on("updateClassificationAggregate", (event, args) => {
event.returnValue = updateAggregate(args.id, args.sort, args.itemCount);
});
}

View File

@ -0,0 +1,12 @@
import Store from "electron-store";
import { getUserDataPath } from ".";
const cacheStore = new Store({
name: "Cache",
clearInvalidConfig: true,
cwd: getUserDataPath(),
});
export default {
cacheStore,
};

View File

@ -0,0 +1,481 @@
import { Result } from "../../../types/common";
import { resolve, dirname, relative, join } from "node:path";
import { mkdirSync, existsSync } from "node:fs";
import mime from "mime";
import retry from "retry";
import request from "request";
import * as cheerio from "cheerio";
import { isAbsolutePath } from "../../../commons/utils/common";
import { BrowserWindow, app, dialog, nativeImage, nativeTheme } from "electron";
import { getRandomUserAgent, iconExts } from "../../commons/utils";
import URI from "urijs";
import { hideMainWindow } from "../main";
import { hideQuickSearchWindow } from "../search";
/**
*
*/
function getProxy() {
if (
global.setting.network.useProxy &&
global.setting.network.proxy.address &&
global.setting.network.proxy.address.trim() !== ""
) {
let uri = new URI(global.setting.network.proxy.address);
if (uri.protocol() && uri.protocol().trim() !== "") {
let address = uri.protocol().toLowerCase() + "://";
if (
global.setting.network.proxy.username &&
global.setting.network.proxy.username.trim() !== "" &&
global.setting.network.proxy.password &&
global.setting.network.proxy.password.trim() !== ""
) {
address +=
global.setting.network.proxy.username +
":" +
global.setting.network.proxy.password +
"@";
}
address += uri.hostname() + ":" + uri.port();
return address;
}
}
return null;
}
/**
*
* @param windowName
* @param url
*/
function downloadImage(windowName: string, url: string) {
let result: Result = {
status: false,
message: global.language.downloadImagePrompt1,
icon: null,
name: null,
};
// 重试
const operation = retry.operation({
retries: 5, // 最多重试 5 次
factor: 1, // 每次重试之间的时间间隔加倍
minTimeout: 1000, // 第一次重试之前等待的时间
maxTimeout: 5000, // 最长等待时间
});
operation.attempt((currentAttempt) => {
// 下载图片
request(
{
uri: url,
proxy: getProxy(),
encoding: null,
timeout: 5000,
headers: {
"User-Agent": getRandomUserAgent(),
},
},
function (error, response, body) {
if (operation.retry(error)) {
return;
}
if (
!error &&
response.statusCode >= 200 &&
response.statusCode <= 299
) {
if (response.headers && response.headers["content-type"]) {
let ext = mime.getExtension(response.headers["content-type"]);
if (iconExts.includes(ext)) {
let buffer = Buffer.from(body);
result.icon =
"data:" +
mime.getType(response.headers["content-type"]) +
";base64," +
buffer.toString("base64");
result.status = true;
result.message = null;
} else {
result.icon = null;
result.status = false;
result.message = global.language.downloadImagePrompt2;
}
}
}
// window
sendToWebContent(windowName, "onDownloadImage", result);
}
);
});
}
/**
*
* @param windowName
* @param url
* @param redirect
*/
function getURLInfo(windowName: string, url: string, redirect: boolean) {
let result: Result = {
status: false,
message: null,
name: null,
icon: null,
};
// 重试
const operation = retry.operation({
retries: 5, // 最多重试 5 次
factor: 1, // 每次重试之间的时间间隔加倍
minTimeout: 1000, // 第一次重试之前等待的时间
maxTimeout: 5000, // 最长等待时间
});
try {
// 发起请求
operation.attempt((currentAttempt) => {
request(
{
uri: url,
proxy: getProxy(),
timeout: 5000,
headers: {
"User-Agent": getRandomUserAgent(),
},
},
function (error, response, body) {
if (operation.retry(error)) {
return;
}
if (
!error &&
response.statusCode >= 200 &&
response.statusCode <= 299
) {
const $ = cheerio.load(body);
// 是否有跳转标签
let refresh = $("meta[http-equiv='refresh']");
// content
let content = refresh.attr("content");
if (content && content.trim() !== "" && redirect) {
// 如果有跳转标签的话,就请求新网址并获取网址信息
let contentSplit = content.split(";");
let urlProperty = contentSplit[contentSplit.length - 1];
let urlPropertySplit = urlProperty.split("=");
let newURL = urlPropertySplit[urlPropertySplit.length - 1];
// 重新获取新网址信息
getURLInfo(windowName, newURL, false);
} else {
// 解析HTML并返回信息
analysisHTML(windowName, url, body);
}
} else {
sendUrlInfo(windowName, result);
}
}
);
});
} catch (e) {
sendUrlInfo(windowName, result);
}
}
/**
* HTML并返回信息
* @param windowName
* @param url
* @param data
*/
function analysisHTML(windowName: string, url: string, data: string) {
let result: Result = {
status: false,
message: null,
name: null,
icon: null,
};
try {
// 解析HTML
let $ = cheerio.load(data);
// 获取标题
result.name = $("head").find("title").text();
// 获取图标URL
let iconURL: string | null = null;
let icon = $("link[rel='icon']");
let href = icon.attr("href");
if (href && href.trim() !== "") {
iconURL = href;
} else {
let shortcutIcon = $("link[rel='shortcut icon']");
let shortcutIconhref = shortcutIcon.attr("href");
if (shortcutIconhref && shortcutIconhref.trim() !== "") {
iconURL = shortcutIconhref;
} else {
iconURL = "/favicon.ico";
}
}
if (iconURL) {
// 去掉类似//www.baidu.com/favicon.ico这样域名的”//“字符
if (iconURL.indexOf("//") === 0) {
iconURL = "http:" + iconURL;
}
// 无协议头,使用当前网址域名
if (iconURL.indexOf("http://") < 0 && iconURL.indexOf("https://") < 0) {
iconURL = url + (iconURL.indexOf("//") === 0 ? "" : "//") + iconURL;
}
// 重试
const operation = retry.operation({
retries: 5, // 最多重试 5 次
factor: 1, // 每次重试之间的时间间隔加倍
minTimeout: 1000, // 第一次重试之前等待的时间
maxTimeout: 5000, // 最长等待时间
});
operation.attempt((currentAttempt) => {
// 下载图标
request(
{
uri: iconURL,
proxy: getProxy(),
encoding: null,
timeout: 5000,
headers: {
"User-Agent": getRandomUserAgent(),
},
},
function (error, response, body) {
if (operation.retry(error)) {
return;
}
if (
!error &&
response.statusCode >= 200 &&
response.statusCode <= 299
) {
let buffer = Buffer.from(body);
result.icon =
"data:" +
mime.getType(iconURL) +
";base64," +
buffer.toString("base64");
result.status = true;
sendUrlInfo(windowName, result);
} else {
sendUrlInfo(windowName, result);
}
}
);
});
} else {
sendUrlInfo(windowName, result);
}
} catch (e) {
sendUrlInfo(windowName, result);
}
}
/**
*
* @param result
*/
function sendUrlInfo(windowName: string, result: Result) {
sendToWebContent(windowName, "onGetURLInfo", result);
}
/**
*
* @param path
*/
function convertPath(path: string) {
let appPath =
process.env.NODE_ENV === "development"
? resolve(".")
: dirname(process.execPath);
if (isAbsolutePath(path)) {
return relative(appPath, path);
} else {
return resolve(appPath, path);
}
}
/**
* IPC到所有窗口
* @param channel
* @param data
*/
function sendAllWindows(channel: string, data: any) {
for (const window of BrowserWindow.getAllWindows()) {
if (!window.isDestroyed()) {
window.webContents.send(channel, data);
}
}
}
/**
*
* @param name
*/
function getWindow(name: string): BrowserWindow | null {
let window: BrowserWindow | null = null;
if (name === "mainWindow") {
window = global.mainWindow;
} else if (name === "quickSearchWindow") {
window = global.quickSearchWindow;
} else if (name === "settingWindow") {
window = global.settingWindow;
} else if (name === "classificationAddEditWindow") {
window = global.classificationAddEditWindow;
} else if (name === "classificationSetIconWindow") {
window = global.classificationSetIconWindow;
} else if (name === "classificationAssociateFolderWindow") {
window = global.classificationAssociateFolderWindow;
} else if (name === "classificationAggregateWindow") {
window = global.classificationAggregateWindow;
} else if (name === "itemAddEditWindow") {
window = global.itemAddEditWindow;
} else if (name === "itemNetworkIconWindow") {
window = global.itemNetworkIconWindow;
} else if (name === "itemSVGIconWindow") {
window = global.itemSVGIconWindow;
} else if (name === "aboutWindow") {
window = global.aboutWindow;
} else if (name === "backupRestoreDataWindow") {
window = global.backupRestoreDataWindow;
}
if (window && !window.isDestroyed()) {
return window;
} else {
return null;
}
}
/**
*
* @param window
*/
function closeWindow(window: BrowserWindow | null) {
if (window && !window.isDestroyed() && window.isVisible()) {
window.close();
}
}
/**
*
*/
function getDot() {
return nativeImage.createFromDataURL(
!nativeTheme.shouldUseDarkColors
? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERJREFUOE9jZKAQMFKon2HUAAa8YWDDwMBQCQ3kdgYGhiPYAhxfIG5lYGDwgmraxsDA4E13Ayj2AlFpbDQh4U9IRAUiAEXYCBFBtkaAAAAAAElFTkSuQmCC"
: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAEtJREFUOE9jZKAQMFKon2HUAAbcYfD//38bBgaGSmggtzMyMh7BFuA4A/H///9bGRgYvKCatjEyMnrT3QDKvEBsAhtNSHgSErGBCAD0gBQREV/HsgAAAABJRU5ErkJggg=="
);
}
/**
*
* @param windowName
* @param listener
* @param params
*/
function sendToWebContent(windowName: string, listener: string, params: any) {
// 获取窗口
let window = getWindow(windowName);
if (window && !window.isDestroyed()) {
window.webContents.send(listener, params);
}
}
/**
*
*/
function closeAllChildProcess() {
if (global.childProcessMap) {
global.childProcessMap.forEach((value, key) => {
try {
value.utilityProcess.kill();
} catch (e) {}
try {
value.port1.close();
} catch (e) {}
try {
value.port2.close();
} catch (e) {}
});
global.childProcessMap.clear();
}
}
/**
*
* @param type
*/
function openAfterHideWindow(type: string) {
if (type === "main" || type === "search") {
if (global.setting.item.openAfterHideMainInterface) {
hideMainWindow();
}
} else if (type === "quickSearch") {
if (global.setting.quickSearch.openAfterHideQuickSearchWindow) {
hideQuickSearchWindow();
}
}
}
/**
*
* @param windowName
* @param message
*/
function showErrorMessageBox(windowName: string, message: string) {
dialog.showMessageBoxSync(getWindow(windowName), {
message: message,
buttons: [global.language.ok],
type: "error",
noLink: true,
});
}
/**
*
*/
function relaunch() {
app.relaunch();
app.quit();
}
// 数据存储目录
function getUserDataPath() {
let userDataPath = app.getPath("userData");
if (
process.env.NODE_ENV !== "development" &&
import.meta.env.VITE_INSTALL === "false"
) {
userDataPath = join(dirname(process.execPath), "data");
if (!existsSync(userDataPath)) {
mkdirSync(userDataPath, { recursive: true });
}
}
return userDataPath;
}
/**
*
*/
function getMainBackgorunColor() {
let backgroundColor = global.setting.appearance.theme.mainBackgroundColor;
if (backgroundColor.length === 9) {
return backgroundColor.substring(0, 7);
} else {
return backgroundColor;
}
}
export {
downloadImage,
getURLInfo,
convertPath,
sendAllWindows,
closeWindow,
getDot,
getWindow,
sendToWebContent,
closeAllChildProcess,
openAfterHideWindow,
showErrorMessageBox,
relaunch,
getUserDataPath,
getMainBackgorunColor,
};

View File

@ -0,0 +1,166 @@
import { app, dialog, ipcMain, OpenDialogSyncOptions, shell } from "electron";
import { getFileIcon } from "../../commons/utils";
import mime from "mime";
import { ShortcutInfo } from "../../../types/common";
import {
convertPath,
downloadImage,
getURLInfo,
sendToWebContent,
showErrorMessageBox,
} from ".";
import { statSync } from "node:fs";
import { getWindow } from "../commons/index";
export default function () {
// emit
ipcMain.on("emit", (event, args) => {
sendToWebContent(args.windowName, args.listener, args.paylod);
});
// 错误提示框
ipcMain.on("showErrorMessageBox", (event, args) => {
showErrorMessageBox(args.windowName, args.message);
});
// 信息提示框
ipcMain.on("showInfoMessageBox", (event, args) => {
dialog.showMessageBoxSync(getWindow(args.windowName), {
message: args.message,
buttons: [global.language.ok],
type: "info",
noLink: true,
});
});
// 对话框
ipcMain.on("showConfirmBox", (event, args) => {
// 弹出对话框
let res = dialog.showMessageBoxSync(getWindow(args.windowName), {
message: args.message,
buttons: [global.language.ok, global.language.cancel],
type: "question",
noLink: true,
cancelId: 1,
});
event.returnValue = res === 0;
});
// 选择文件
ipcMain.on("selectFile", (event, args) => {
// 窗口名称
let windowName: string = args.windowName;
// 是否寻找目标
let target: boolean = args.target;
// 默认路径
let defaultPath: string | null = args.defaultPath;
// 参数
let options: OpenDialogSyncOptions = {};
if (defaultPath && defaultPath.trim() !== "") {
options.defaultPath = defaultPath;
} else {
options.defaultPath = app.getPath("desktop");
}
let filePathList = dialog.showOpenDialogSync(
getWindow(windowName),
options
);
if (filePathList && filePathList.length > 0) {
let filePath = filePathList[0];
if (target) {
if (mime.getType(filePath) === "application/x-ms-shortcut") {
// 获取真实文件路径和参数
let shortcutInfo: ShortcutInfo | null =
global.addon.getShortcutFileInfo(filePath);
if (shortcutInfo && shortcutInfo.target) {
// 路径
filePath = shortcutInfo.target;
}
}
}
event.returnValue = filePath;
} else {
event.returnValue = null;
}
});
// 选择文件夹
ipcMain.on("selectDirectory", (event, args) => {
// 窗口名称
let windowName: string = args.windowName;
// 默认路径
let defaultPath: string | null = args.defaultPath;
// 参数
let options: OpenDialogSyncOptions = {
properties: ["openDirectory"],
};
if (defaultPath && defaultPath.trim() !== "") {
options.defaultPath = defaultPath;
} else {
options.defaultPath = app.getPath("desktop");
}
let dirPathList = dialog.showOpenDialogSync(getWindow(windowName), options);
if (dirPathList && dirPathList.length > 0) {
let dirPath = dirPathList[0];
event.returnValue = dirPath;
} else {
event.returnValue = null;
}
});
// 获取图标
ipcMain.on("getFileIcon", (event, args) => {
// 窗口名称
let windowName: string = args.windowName;
// 路径
let filePath: string | null = args.path;
if (filePath) {
// 图标
let icon: string | null = getFileIcon(filePath);
// 发送到页面
sendToWebContent(windowName, "onGetFileIcon", icon);
}
});
// 下载图片
ipcMain.on("downloadImage", (event, args) => {
downloadImage(args.windowName, args.url);
});
// 获取网址信息
ipcMain.on("getURLInfo", (event, args) => {
getURLInfo(args.windowName, args.url, true);
});
// 转换路径
ipcMain.on("convertPath", (event, args) => {
event.returnValue = convertPath(args.path);
});
// 路径是否存在
ipcMain.on("pathExist", (event, args) => {
try {
statSync(args.path);
event.returnValue = true;
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
event.returnValue = false;
}
});
// 是否是文件
ipcMain.on("isFile", (event, args) => {
try {
const stats = statSync(args.path);
event.returnValue = stats.isFile();
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
event.returnValue = true;
}
});
// 打开URL
ipcMain.on("openURL", (event, args) => {
shell.openExternal(args);
});
// 获取版本
ipcMain.on("getVersion", (event, args) => {
event.returnValue = app.getVersion();
});
// 退出
ipcMain.on("exit", () => {
app.quit();
});
}

486
electron/main/data/data.ts Normal file
View File

@ -0,0 +1,486 @@
import Database from "better-sqlite3-multiple-ciphers";
import {
getCustomDataSqlite3,
getDataSqlite3,
} from "../../commons/betterSqlite3";
import Store from "electron-store";
import { extname, parse } from "node:path";
import {
newClassification,
newClassificationData,
newItem,
newItemData,
} from "../../../commons/utils/common";
import { add as addClassification } from "../classification/data";
import { add as addItem } from "../item/data";
import { getSetting } from "../../../commons/utils/setting";
import { add as addSetting } from "../setting/data";
/**
*
* @param filePath
*/
function restore(filePath: string) {
// 获取文件后缀
let ext = extname(filePath);
if (ext.toLowerCase() === ".db") {
// 数据库文件
return databaseRestore(filePath);
} else if (ext.toLowerCase() === ".json") {
// 旧版JSON
return jsonRestore(filePath);
}
return false;
}
/**
*
* @param filePath
* @returns
*/
function databaseRestore(filePath: string) {
try {
// 获取导入DB
let importDB = getCustomDataSqlite3(filePath);
// 查询分类数据
let classificationList = importDB
.prepare("SELECT * FROM classification")
.all();
// 查询项目数据
let itemList = importDB.prepare("SELECT * FROM item").all();
// 查询设置数据
let setting = importDB.prepare("SELECT * FROM setting").all();
// 查询ID索引表数据
let sequence = importDB.prepare("SELECT * FROM sqlite_sequence").all();
// 获取当前DB
let db = getDataSqlite3();
// 开启事务
db.transaction(() => {
// 清空并导入数据
clearAndInsert(db, "classification", classificationList);
clearAndInsert(db, "item", itemList);
clearAndInsert(db, "setting", setting);
clearAndInsert(db, "sqlite_sequence", sequence);
})();
return true;
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
return false;
}
/**
*
* @param tableName
* @param list
*/
function clearAndInsert(
db: Database.Database,
tableName: string,
list: Array<any>
) {
// 清空数据
db.prepare(`DELETE FROM ${tableName}`).run();
// 插入数据
list.forEach((row) => {
const keys = Object.keys(row);
const columns = keys.map((key) => `\`${key}\``);
const placeholders = keys.map(() => "?").join(",");
const values = keys.map((key) => row[key]);
db.prepare(
`INSERT INTO ${tableName} (${columns.join(",")}) VALUES (${placeholders})`
).run(values);
});
}
/**
* JSON恢复数据
* @param filePath
* @returns
*/
function jsonRestore(filePath: string) {
try {
// 获取当前DB
let db = getDataSqlite3();
// 解析路径
let pathParse = parse(filePath);
// 读取JSON
const store = new Store({
name: pathParse.name,
cwd: pathParse.dir,
fileExtension: pathParse.ext.replace(".", ""),
encryptionKey: "0b52eb58-4c0f-5ff1-b062-031546a8d269",
});
// 开启事务
db.transaction(() => {
// 图标数据
let iconData = store.get("iconData") as Array<any>;
// 清空数据
db.prepare(`DELETE FROM classification`).run();
db.prepare(`DELETE FROM item`).run();
db.prepare(`DELETE FROM setting`).run();
db.prepare(`DELETE FROM sqlite_sequence`).run();
// 导入数据
let list = store.get("list") as Array<any>;
if (list && list.length > 0) {
for (let i = 0; i < list.length; i++) {
let parent = list[i];
// 添加分类
let classification = jsonAddClassification(parent, null);
if (classification) {
if (parent.childList && parent.childList.length > 0) {
// 子分类
for (let j = 0; j < parent.childList.length; j++) {
const child = parent.childList[j];
let childClassification = jsonAddClassification(
child,
classification.id
);
if (
childClassification &&
child.itemList &&
child.itemList.length > 0
) {
// 项目
for (let k = 0; k < child.itemList.length; k++) {
jsonAddItem(
child.itemList[k],
child,
childClassification.id,
iconData
);
}
}
}
} else {
if (parent.itemList && parent.itemList.length > 0) {
// 项目
for (let k = 0; k < parent.itemList.length; k++) {
jsonAddItem(
parent.itemList[k],
parent,
classification.id,
iconData
);
}
}
}
}
}
}
// 设置
if (store.get("setting")) {
jsonAddSetting(store.get("setting"));
}
})();
return true;
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
return false;
}
/**
*
* @param oldClassification
* @param parentId
* @returns
*/
function jsonAddClassification(
oldClassification: any,
parentId: number | null
) {
// 排序
let itemSort: "default" | "initial" | "openNumber" | "lastOpen" = "default";
// 如果是聚合分类的话,获取聚合分类排序,否则就获取普通排序
if (
oldClassification.aggregateSort &&
oldClassification.aggregateSort.trim() != "" &&
(oldClassification.aggregateSort == "initial" ||
oldClassification.aggregateSort == "openNumber" ||
oldClassification.aggregateSort == "lastOpen")
) {
// 聚合分类
itemSort = oldClassification.aggregateSort;
} else if (
oldClassification.sort == "initial" ||
oldClassification.sort == "openNumber" ||
oldClassification.sort == "lastOpen"
) {
// 普通
itemSort = oldClassification.sort;
}
let data = newClassificationData({
icon: oldClassification.icon,
excludeSearch: oldClassification.excludeSearch,
itemLayout:
oldClassification.layout == "tile" || oldClassification.layout == "list"
? oldClassification.layout
: "default",
itemSort: itemSort,
itemColumnNumber: oldClassification.columnNumber,
itemIconSize: oldClassification.iconSize,
itemShowOnly:
oldClassification.showOnly == "file" ||
oldClassification.showOnly == "folder"
? oldClassification.showOnly
: "default",
associateFolderHiddenItems: oldClassification.hiddenItem,
associateFolderPath: oldClassification.mapDirectory,
aggregateItemCount: oldClassification.aggregateItemNumber,
});
// 类型 0:普通分类 1:关联文件夹 2:聚合分类
let type = 0;
if (
oldClassification.mapDirectory &&
oldClassification.mapDirectory.trim() != ""
) {
// 关联文件夹
type = 1;
} else if (
oldClassification.aggregateSort &&
oldClassification.aggregateSort.trim() != ""
) {
// 聚合分类
type = 2;
}
let classification = newClassification({
parentId,
name: oldClassification.name,
type,
data,
shortcutKey: oldClassification.shortcutKey,
globalShortcutKey: oldClassification.globalShortcutKey,
});
return addClassification(
classification.parentId,
classification.name,
classification.shortcutKey,
classification.globalShortcutKey,
classification.data,
classification.type
);
}
/**
*
* @param oldItem
* @param oldClassification
* @param classificationId
* @param iconData
*/
function jsonAddItem(
oldItem: any,
oldClassification: any,
classificationId: number,
iconData: Array<any>
) {
// 类型
let type = oldItem.type;
if (type == 4) {
type = 5;
} else if (type == 5) {
type = 4;
}
// 多项目不导入
if (type == 5) {
return null;
}
// 合并目标
let target = oldItem.path;
if (oldItem.url && oldItem.url.trim() != "") {
// 网址
target = oldItem.url;
} else if (oldItem.shell && oldItem.shell.trim() != "") {
// 系统
target = oldItem.shell;
}
// 图标
let icon = oldItem.icon;
if (iconData) {
for (let i = 0; i < iconData.length; i++) {
const data = iconData[i];
if (oldClassification.parentId) {
if (
data.classificationParentId == oldClassification.parentId &&
data.classificationChildId == oldClassification.id &&
data.itemId == oldItem.id &&
data.icon &&
data.icon.trim() != ""
) {
icon = data.icon;
}
} else {
if (
data.classificationParentId == oldClassification.id &&
data.itemId == oldItem.id &&
data.icon &&
data.icon.trim() != ""
) {
icon = data.icon;
}
}
}
}
let data = newItemData({
startLocation: oldItem.startLocation,
target,
params: oldItem.params,
runAsAdmin: oldItem.admin,
icon,
htmlIcon:
!oldItem.htmlIcon || oldItem.htmlIcon.trim() == ""
? null
: oldItem.htmlIcon,
remark: oldItem.remark,
iconBackgroundColor: oldItem.useAppxBackgroundColor,
fixedIcon: oldItem.notRefreshIcon,
openNumber: oldItem.openNumber,
lastOpen: oldItem.lastOpen,
quickSearchOpenNumber: oldItem.quickSearchOpenNumber,
quickSearchLastOpen: oldItem.quickSearchLastOpen,
});
let item = newItem({
classificationId,
name: oldItem.name,
type,
data,
shortcutKey: oldItem.shortcutKey,
globalShortcutKey: oldItem.globalShortcutKey,
});
return addItem(item, false);
}
/**
*
* @param oldSetting
*/
function jsonAddSetting(oldSetting: any) {
let setting = getSetting(null);
// 常规
if (oldSetting.general) {
setting.general.startup = oldSetting.general.startup;
setting.general.startupTray = oldSetting.general.startupTray;
setting.general.showHideShortcutKey =
oldSetting.general.showHideShortcutKey;
setting.general.alwaysTop = oldSetting.general.alwaysTop;
setting.general.edgeAutoHide = oldSetting.general.edgeAutoHide;
setting.general.lockSize = oldSetting.general.lockSize;
setting.general.hideLoseFocus = oldSetting.general.hideLosingFocus;
setting.general.hideTray = oldSetting.general.hideTray;
setting.general.showHideMouseWheelClick =
oldSetting.general.showHideMouseWheelClick;
setting.general.fixedPosition = oldSetting.general.fixedPosition;
setting.general.alwaysCenter = oldSetting.general.alwaysCenter;
setting.general.showFollowMousePosition =
oldSetting.general.showFollowMousePosition;
setting.general.notDisturb = oldSetting.general.notDisturb;
setting.general.showHideDoubleClickTaskbar =
oldSetting.general.doubleClickTaskbar;
setting.general.delayDisplayMs = oldSetting.general.delayDisplayMS;
setting.general.delayHideMs = oldSetting.general.delayHidingMS;
setting.general.switchEnglish = oldSetting.general.switchEnglish;
if (oldSetting.item) {
setting.general.searchShowHideShortcutKey =
oldSetting.item.searchShortcutKey;
}
}
// 分类
if (oldSetting.classification) {
setting.classification.width = oldSetting.classification.width;
setting.classification.layout = oldSetting.classification.layout;
setting.classification.mouseHover = oldSetting.classification.mouseHover;
setting.classification.mouseHoverMs =
oldSetting.classification.mouseHoverMS;
setting.classification.mouseWheel = oldSetting.classification.mouseWheel;
setting.classification.rememberSelectionState =
oldSetting.classification.rememberSelectionState;
setting.classification.nameAlign = oldSetting.classification.nameAlign;
setting.classification.mode = oldSetting.classification.mode;
setting.classification.autoSwitchClassification =
oldSetting.classification.autoSwitchClassification;
setting.classification.hideWindowCollapseSubClassification =
oldSetting.classification.hideWindowFoldChildClassification;
setting.classification.switchClassificationCollapseOtherSubClassification =
oldSetting.classification.switchClassificationCollapseOtherSubClassification;
}
// 项目
if (oldSetting.item) {
setting.item.layout = oldSetting.item.layout;
setting.item.iconSize = oldSetting.item.iconSize;
setting.item.doubleClickOpen = oldSetting.item.doubleClickRunItem;
setting.item.openAfterHideMainInterface =
oldSetting.item.openAfterHideMainInterface;
setting.item.useItemOpen = oldSetting.item.useItemOpen;
setting.item.openNumber = oldSetting.item.openNumber;
setting.item.hideItemName = oldSetting.item.hideItemName;
setting.item.hideEllipsis = oldSetting.item.hideEllipsis;
setting.item.itemNameRowCount = oldSetting.item.itemNameRowCount;
setting.item.width = oldSetting.item.width;
setting.item.columnNumber = oldSetting.item.columnNumber;
setting.item.checkInvalidItem = oldSetting.item.checkInvalidItem;
setting.item.fontSize = oldSetting.item.fontSize;
setting.item.fontWeight = oldSetting.item.fontWeight;
setting.item.fontLineHeight = oldSetting.item.fontLineHeight;
}
// 子分类
if (oldSetting.subClassification) {
setting.subClassification.itemAreaNameFontSize =
oldSetting.subClassification.itemAreaNameFontSize;
setting.subClassification.itemAreaNameFontWeight =
oldSetting.subClassification.itemAreaNameFontWeight;
setting.subClassification.itemAreaNameFontLineHeight =
oldSetting.subClassification.itemAreaNameFontLineHeight;
}
// 快速搜索
if (oldSetting.quickSearch) {
setting.quickSearch.enable = oldSetting.quickSearch.enable;
setting.quickSearch.showHideShortcutKey =
oldSetting.quickSearch.showHideShortcutKey;
setting.quickSearch.openShortcutKey =
oldSetting.quickSearch.openShortcutKey;
setting.quickSearch.hideLoseFocus = oldSetting.quickSearch.hideLosingFocus;
setting.quickSearch.openNow = oldSetting.quickSearch.openNow;
setting.quickSearch.showHistory = oldSetting.quickSearch.showHistory;
setting.quickSearch.showHistorySort =
oldSetting.quickSearch.showHistorySort;
setting.quickSearch.useItemOpen = oldSetting.quickSearch.useItemOpen;
setting.quickSearch.openAfterHideQuickSearchWindow =
oldSetting.quickSearch.openAfterHideQuickSearchWindow;
setting.quickSearch.matchConditionsRemark =
oldSetting.quickSearch.matchingConditionsRemark;
}
// 网络搜索
if (oldSetting.webSearch) {
setting.webSearch.mode = oldSetting.webSearch.mode;
if (oldSetting.webSearch.searchSourceList) {
setting.webSearch.searchSourceList = [];
for (let i = 0; i < oldSetting.webSearch.searchSourceList.length; i++) {
const oldSearchSource = oldSetting.webSearch.searchSourceList[i];
setting.webSearch.searchSourceList.push({
id: oldSearchSource.id,
keyword: oldSearchSource.keyword,
name: oldSearchSource.name,
url: oldSearchSource.URL,
description: oldSearchSource.description,
});
}
}
}
// 网络
if (oldSetting.network) {
setting.network.useProxy = oldSetting.network.useProxy;
if (oldSetting.network.proxy) {
setting.network.proxy.address = oldSetting.network.proxy.address;
setting.network.proxy.username = oldSetting.network.proxy.username;
setting.network.proxy.password = oldSetting.network.proxy.password;
}
}
return addSetting(getSetting(setting));
}
export { restore };

View File

@ -0,0 +1,71 @@
import { BrowserWindow, shell } from "electron";
import { closeWindow, getMainBackgorunColor } from "../commons";
import { join } from "node:path";
import { getDataSqlite3 } from "../../commons/betterSqlite3";
// 窗口
let backupRestoreDataWindow: BrowserWindow | null = null;
/**
* /
*/
function createBackupRestoreDataWindow() {
// 如果窗口存在先关闭窗口
closeWindow(backupRestoreDataWindow);
// 创建窗口
backupRestoreDataWindow = global.backupRestoreDataWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 108,
width: 400,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
backupRestoreDataWindow.loadURL(
process.env.VITE_DEV_SERVER_URL + "Data/BackupRestore"
);
} else {
backupRestoreDataWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/Data/BackupRestore",
});
}
backupRestoreDataWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
backupRestoreDataWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
backupRestoreDataWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
backupRestoreDataWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
*/
function backupData(filePath: string) {
// 获取数据库
let db = getDataSqlite3();
// 备份数据
return db.backup(filePath);
}
export { createBackupRestoreDataWindow, backupData };

View File

@ -0,0 +1,75 @@
import { dialog, ipcMain } from "electron";
import { backupData, createBackupRestoreDataWindow } from ".";
import { closeWindow, relaunch, showErrorMessageBox } from "../commons";
import { restore } from "./data";
export default function () {
// 创建备份/恢复数据窗口
ipcMain.on("createBackupRestoreDataWindow", (event, args) => {
createBackupRestoreDataWindow();
});
// 显示备份/恢复数据窗口
ipcMain.on("showBackupRestoreDataWindow", (event, args) => {
if (global.backupRestoreDataWindow) {
global.backupRestoreDataWindow.show();
}
});
// 关闭备份/恢复数据窗口
ipcMain.on("closeBackupRestoreDataWindow", (event, args) => {
closeWindow(global.backupRestoreDataWindow);
});
// 备份数据
ipcMain.on("backupData", () => {
try {
let filePath = dialog.showSaveDialogSync(global.backupRestoreDataWindow, {
defaultPath: "Data",
filters: [{ name: "DB", extensions: ["db"] }],
});
if (filePath && filePath.trim() !== "") {
backupData(filePath).finally(() => {
// 关闭备份/恢复窗口
closeWindow(global.backupRestoreDataWindow);
});
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
});
// 恢复数据
ipcMain.on("restoreData", () => {
try {
let filePathList = dialog.showOpenDialogSync(
global.backupRestoreDataWindow,
{
filters: [{ name: "Data", extensions: ["db", "json"] }],
}
);
if (filePathList && filePathList.length > 0) {
let filePath = filePathList[0];
if (restore(filePath)) {
// 清空localStorage
global.mainWindow.webContents.session.clearStorageData({
storages: ["localstorage"],
});
// 重新启动程序
relaunch();
} else {
showErrorMessageBox(
"backupRestoreDataWindow",
global.language.restoreDataPrompt
);
}
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
showErrorMessageBox(
"backupRestoreDataWindow",
global.language.restoreDataPrompt
);
}
});
}

159
electron/main/index.ts Normal file
View File

@ -0,0 +1,159 @@
import { app, BrowserWindow, dialog } from "electron";
import { release } from "node:os";
import { join, dirname } from "node:path";
import indexIpcEvent from "./main/ipcEvent";
import classificationIpcEvent from "./classification/ipcEvent";
import { init as classificationDataInit } from "./classification/data";
import { init as itemDataInit } from "./item/data";
import { initSystemItem } from "./item/commons/data";
import commonIpcEvent from "./commons/ipcEvent";
import itemIpcEvent from "./item/ipcEvent";
import settingIpcEvent from "./setting/ipcEvent";
import { init as settingDataInit } from "./setting/data";
import { setShortcutKey } from "./setting";
import searchIpcEvent from "./search/ipcEvent";
import { createMainWindow } from "./main";
import { closeAllChildProcess } from "./commons";
import { createQuickSearchWindow } from "./search";
import { getLanguage } from "../../commons/data/languages";
import aboutIpcEvent from "./about/ipcEvent";
import dataIpcEvent from "./data/ipcEvent";
// 数据存储目录
if (
process.env.NODE_ENV !== "development" &&
import.meta.env.VITE_INSTALL === "false"
) {
app.setPath("appData", join(dirname(process.execPath), "data"));
app.setPath("userData", join(dirname(process.execPath), "data"));
}
process.env.DIST_ELECTRON = join(__dirname, "..");
process.env.DIST = join(process.env.DIST_ELECTRON, "../dist");
process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL
? join(process.env.DIST_ELECTRON, "../public")
: process.env.DIST;
// Disable GPU Acceleration for Windows 7
if (release().startsWith("6.1")) app.disableHardwareAcceleration();
// Set application name for Windows 10+ notifications
if (process.platform === "win32") app.setAppUserModelId(app.getName());
if (!app.requestSingleInstanceLock()) {
app.quit();
process.exit(0);
}
// Remove electron security warnings
// This warning only shows in development mode
// Read more on https://www.electronjs.org/docs/latest/tutorial/security
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
app.whenReady().then(() => {
try {
// addon
global.addon = require("../../native/addon.node");
// 初始化数据
settingDataInit();
// 获取语言
global.language = getLanguage(global.setting.general.language);
// 禁用debugtron
for (let i = 0; i < process.argv.length; i++) {
const arg = process.argv[i];
if (
arg.indexOf("--inspect") !== -1 ||
arg.indexOf("--remote-debugging-port") !== -1
) {
dialog.showMessageBoxSync(null, {
message: "达咩呦达咩达咩~",
buttons: [global.language.ok],
type: "error",
noLink: true,
});
app.quit();
return;
}
}
// 禁止多开
const instanceLock = app.requestSingleInstanceLock();
if (!instanceLock) {
app.quit();
return;
}
// 初始化数据
classificationDataInit();
itemDataInit();
initSystemItem();
// 初始化监听
indexIpcEvent();
commonIpcEvent();
classificationIpcEvent();
itemIpcEvent();
settingIpcEvent();
searchIpcEvent();
aboutIpcEvent();
dataIpcEvent();
// 创建主窗口
createMainWindow();
if (global.setting.quickSearch.enable) {
// 创建快速搜索窗口
createQuickSearchWindow();
}
// 设置快捷键
setShortcutKey();
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
} else {
dialog.showMessageBoxSync({
type: "error",
title: "Dawn Launcher",
message: e.stack,
});
app.quit();
}
}
});
// 全局异常
process.on("uncaughtException", (err) => {
dialog.showMessageBoxSync({
type: "error",
title: "Dawn Launcher",
message: err.stack,
});
// 关闭所有子进程
closeAllChildProcess();
// 退出
app.quit();
});
app.on("before-quit", () => {
// 关闭所有子进程
closeAllChildProcess();
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
app.on("second-instance", () => {
if (mainWindow) {
if (!mainWindow.isVisible()) {
mainWindow.show();
mainWindow.focus();
global.blurHide = true;
} else {
mainWindow.focus();
}
}
});
app.on("activate", () => {
const allWindows = BrowserWindow.getAllWindows();
if (allWindows.length) {
allWindows[0].focus();
} else {
createMainWindow();
}
});

View File

@ -0,0 +1,561 @@
import { CommonItem } from "../../../../types/item";
import {
newCommonItem,
newCommonItemData,
} from "../../../../commons/utils/common";
import {
calcIcon,
fileExplorerIcon,
networkSharingCenterIcon,
powerIcon,
} from "../../../commons/constants";
import { getCacheDataSqlite3 } from "../../../commons/betterSqlite3";
// 获取数据库
let db = getCacheDataSqlite3();
// 系统项目表名
let systenItemTableName = "system_item";
// 开始菜单项目表名
let startMenuItemTableName = "start_menu_item";
/**
*
*/
function getCommonItem(row: any): CommonItem {
let data = JSON.parse(row.data);
return newCommonItem({
id: row.id,
name: row.name,
data: newCommonItemData({
target: data.target,
params: data.params,
icon: data.icon,
htmlIcon: data.htmlIcon,
}),
});
}
/**
*
*/
function initSystemItemTable() {
// sql
let sql = `CREATE TABLE IF NOT EXISTS ${systenItemTableName} (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
data TEXT NOT NULL,
\`order\` INTEGER NOT NULL)`;
// 运行
db.exec(sql);
}
/**
*
*/
function initSystemItem() {
// 初始化表
initSystemItemTable();
// 初始化数据
// 计算机
let computer = selectById(systenItemTableName, 1);
if (!computer) {
let icon: string | null = global.addon.getFileIcon(
"shell:MyComputerFolder"
);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 1,
name: global.language.computer,
data: newCommonItemData({
target: "shell:MyComputerFolder",
icon: icon,
}),
order: 1,
})
);
}
}
// 文档
let document = selectById(systenItemTableName, 2);
if (!document) {
let icon: string | null = global.addon.getFileIcon("shell:Local Documents");
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 2,
name: global.language.documents,
data: newCommonItemData({
target: "shell:Local Documents",
icon: icon,
}),
order: 2,
})
);
}
}
// 控制面板
let controlPanel = selectById(systenItemTableName, 3);
if (!controlPanel) {
let icon: string | null = global.addon.getFileIcon(
"shell:ControlPanelFolder"
);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 3,
name: global.language.controlPanel,
data: newCommonItemData({
target: "shell:ControlPanelFolder",
icon: icon,
}),
order: 3,
})
);
}
}
// 网络和共享中心
let networkSharingCenter = selectById(systenItemTableName, 4);
if (!networkSharingCenter) {
add(
systenItemTableName,
newCommonItem({
id: 4,
name: global.language.networkShareCenter,
data: newCommonItemData({
target: "control.exe",
params: "/name Microsoft.NetworkAndSharingCenter",
icon: networkSharingCenterIcon,
}),
order: 4,
})
);
}
// 回收站
let recyleBin = selectById(systenItemTableName, 5);
if (!recyleBin) {
let icon: string | null = global.addon.getFileIcon(
"shell:RecycleBinFolder"
);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 5,
name: global.language.recycleBin,
data: newCommonItemData({
target: "shell:RecycleBinFolder",
icon: icon,
}),
order: 5,
})
);
}
}
// 文件资源管理器
let fileExplorer = selectById(systenItemTableName, 6);
if (!fileExplorer) {
add(
systenItemTableName,
newCommonItem({
id: 6,
name: global.language.fileExplorer,
data: newCommonItemData({
target: "control.exe",
params: "folders",
icon: fileExplorerIcon,
}),
order: 6,
})
);
}
// 程序和功能
let programFeatures = selectById(systenItemTableName, 7);
if (!programFeatures) {
let icon: string | null = global.addon.getFileIcon(
"shell:ChangeRemoveProgramsFolder"
);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 7,
name: global.language.programsFeatures,
data: newCommonItemData({
target: "shell:ChangeRemoveProgramsFolder",
icon: icon,
}),
order: 7,
})
);
}
}
// 文件资源管理器
let calc = selectById(systenItemTableName, 8);
if (!calc) {
add(
systenItemTableName,
newCommonItem({
id: 8,
name: global.language.calculator,
data: newCommonItemData({
target: "calc.exe",
icon: calcIcon,
}),
order: 8,
})
);
}
// 服务
let services = selectById(systenItemTableName, 9);
if (!services) {
let path: string | null = global.addon.searchPath("services.msc");
if (path) {
let icon: string | null = global.addon.getFileIcon(path);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 9,
name: global.language.services,
data: newCommonItemData({
target: "services.msc",
icon: icon,
}),
order: 9,
})
);
}
}
}
// 命令提示符
let cmd = selectById(systenItemTableName, 10);
if (!cmd) {
let path: string | null = global.addon.searchPath("cmd.exe");
if (path) {
let icon: string | null = global.addon.getFileIcon(path);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 10,
name: global.language.commandPrompt,
data: newCommonItemData({
target: "cmd.exe",
icon: icon,
}),
order: 10,
})
);
}
}
}
// 任务管理器
let taskmgr = selectById(systenItemTableName, 11);
if (!taskmgr) {
let path: string | null = global.addon.searchPath("taskmgr.exe");
if (path) {
let icon: string | null = global.addon.getFileIcon(path);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 11,
name: global.language.taskManager,
data: newCommonItemData({
target: "taskmgr.exe",
icon: icon,
}),
order: 11,
})
);
}
}
}
// 注册表编辑
let regedit = selectById(systenItemTableName, 12);
if (!regedit) {
let path: string | null = global.addon.searchPath("regedit.exe");
if (path) {
let icon: string | null = global.addon.getFileIcon(path);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 12,
name: global.language.registryEditor,
data: newCommonItemData({
target: "regedit.exe",
icon: icon,
}),
order: 12,
})
);
}
}
}
// 电源选项
let powercfg = selectById(systenItemTableName, 13);
if (!powercfg) {
add(
systenItemTableName,
newCommonItem({
id: 13,
name: global.language.powerOptions,
data: newCommonItemData({
target: "control.exe",
params: "powercfg.cpl",
icon: powerIcon,
}),
order: 13,
})
);
}
// 资源监视器
let perfmon = selectById(systenItemTableName, 14);
if (!perfmon) {
let path: string | null = global.addon.searchPath("perfmon.exe");
if (path) {
let icon: string | null = global.addon.getFileIcon(path);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 14,
name: global.language.resourceMonitor,
data: newCommonItemData({
target: "perfmon.exe",
params: "/res",
icon: icon,
}),
order: 14,
})
);
}
}
}
// 计算机管理
let compmgmt = selectById(systenItemTableName, 15);
if (!compmgmt) {
let path: string | null = global.addon.searchPath("compmgmt.msc");
if (path) {
let icon: string | null = global.addon.getFileIcon(path);
if (icon) {
add(
systenItemTableName,
newCommonItem({
id: 15,
name: global.language.computerManagement,
data: newCommonItemData({
target: "compmgmt.msc",
params: "/s",
icon: icon,
}),
order: 15,
})
);
}
}
}
// 关机
let shutdown = selectById(systenItemTableName, 16);
if (!shutdown) {
add(
systenItemTableName,
newCommonItem({
id: 16,
name: global.language.shutdown,
data: newCommonItemData({
target: "shutdown",
params: "-s -t 0",
htmlIcon:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" style="width: 100%; height: 100%;"><path d="M12 3c-.55 0-1 .45-1 1v8c0 .55.45 1 1 1s1-.45 1-1V4c0-.55-.45-1-1-1zm5.14 2.86a.99.99 0 0 0-.01 1.39c1.13 1.2 1.83 2.8 1.87 4.57c.09 3.83-3.08 7.13-6.91 7.17A6.981 6.981 0 0 1 5 12c0-1.84.71-3.51 1.87-4.76c.37-.39.37-1-.01-1.38a.993.993 0 0 0-1.43.02A8.92 8.92 0 0 0 3 11.74c-.14 4.88 3.83 9.1 8.71 9.25c5.1.16 9.29-3.93 9.29-9c0-2.37-.92-4.51-2.42-6.11c-.38-.41-1.04-.42-1.44-.02z" fill="currentColor"></path></svg>',
}),
order: 16,
})
);
}
// 重启
let restart = selectById(systenItemTableName, 17);
if (!restart) {
add(
systenItemTableName,
newCommonItem({
id: 17,
name: global.language.restart,
data: newCommonItemData({
target: "shutdown",
params: "-r -t 0",
htmlIcon:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" style="width: 100%; height: 100%;"><path d="M12 4V2.21c0-.45-.54-.67-.85-.35l-2.8 2.79c-.2.2-.2.51 0 .71l2.79 2.79c.32.31.86.09.86-.36V6c3.31 0 6 2.69 6 6c0 .79-.15 1.56-.44 2.25c-.15.36-.04.77.23 1.04c.51.51 1.37.33 1.64-.34c.37-.91.57-1.91.57-2.95c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6c0-.79.15-1.56.44-2.25c.15-.36.04-.77-.23-1.04c-.51-.51-1.37-.33-1.64.34C4.2 9.96 4 10.96 4 12c0 4.42 3.58 8 8 8v1.79c0 .45.54.67.85.35l2.79-2.79c.2-.2.2-.51 0-.71l-2.79-2.79a.5.5 0 0 0-.85.36V18z" fill="currentColor"></path></svg>',
}),
order: 17,
})
);
}
// 睡眠
let sleep = selectById(systenItemTableName, 18);
if (!sleep) {
add(
systenItemTableName,
newCommonItem({
id: 18,
name: global.language.sleep,
data: newCommonItemData({
target: "rundll32.exe",
params: "powrprof.dll, SetSuspendState Sleep",
htmlIcon:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" style="width: 100%; height: 100%;"><path d="M14 4c.34 0 .68.02 1.01.07C13.1 6.23 12 9.05 12 12s1.1 5.77 3.01 7.93c-.33.05-.67.07-1.01.07c-4.41 0-8-3.59-8-8s3.59-8 8-8m0-2C8.48 2 4 6.48 4 12s4.48 10 10 10c1.82 0 3.53-.5 5-1.35c-2.99-1.73-5-4.95-5-8.65s2.01-6.92 5-8.65A9.973 9.973 0 0 0 14 2z" fill="currentColor"></path></svg>',
}),
order: 18,
})
);
}
// 锁定
let lock = selectById(systenItemTableName, 19);
if (!lock) {
add(
systenItemTableName,
newCommonItem({
id: 19,
name: global.language.lock,
data: newCommonItemData({
target: "rundll32.exe",
params: "user32.dll LockWorkStation",
htmlIcon:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" style="width: 100%; height: 100%;"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6zm9 14H6V10h12v10zm-6-3c1.1 0 2-.9 2-2s-.9-2-2-2s-2 .9-2 2s.9 2 2 2z" fill="currentColor"></path></svg>',
}),
order: 19,
})
);
}
// 关闭显示器
let turnOffMonitor = selectById(systenItemTableName, 20);
if (!turnOffMonitor) {
add(
systenItemTableName,
newCommonItem({
id: 20,
name: global.language.turnOffMonitor,
data: newCommonItemData({
target: "static:TurnOffMonitor",
htmlIcon:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" style="width: 100%; height: 100%;"><path d="M20 3H4c-1.1 0-2 .9-2 2v11c0 1.1.9 2 2 2h3c-.55.55-1 .87-1 1.59c0 .78.63 1.41 1.41 1.41h9.17c.78 0 1.41-.63 1.41-1.41c0-.72-.44-1.03-1-1.59h3c1.1 0 2-.9 2-2V5C22 3.9 21.1 3 20 3zm0 13H4V5h16v11z" fill="currentColor"></path></svg>',
}),
order: 20,
})
);
}
}
/**
*
*/
function initStartMenuItemTable() {
// sql
let sql = `CREATE TABLE IF NOT EXISTS ${startMenuItemTableName} (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
data TEXT NOT NULL,
\`order\` INTEGER NOT NULL)`;
// 运行
db.exec(sql);
}
/**
*
* @param table
*/
function list(table: string) {
// sql
let sql = `SELECT id, name, data, \`order\` FROM ${table}`;
// 查询
let list = db.prepare(sql).all();
// 返回
return list.map((row) => {
return getCommonItem(row);
});
}
/**
* ID查询
* @param table
* @param id
*/
function selectById(table: string, id: number): CommonItem | null {
// SQL
let sql = `SELECT id, name, data, \`order\` FROM ${table} WHERE id = ?`;
// 运行
let row = db.prepare(sql).get(id);
// 返回
if (row) {
return getCommonItem(row);
} else {
return null;
}
}
/**
*
* @param table
* @param commonItem
* @returns
*/
function add(table: string, commonItem: CommonItem) {
// SQL
let sql = `INSERT INTO ${table} (id, name, data, \`order\`) VALUES (?, ?, ?, ?)`;
// 运行
db.prepare(sql).run(
commonItem.id,
commonItem.name,
JSON.stringify(commonItem.data),
commonItem.order
);
}
/**
*
* @param table
*/
function deleteAll(table: string) {
// SQL
let sql = `DELETE FROM ${table}`;
// 运行
db.prepare(sql).run();
}
/**
*
* @param list
*/
function batchAdd(list: Array<CommonItem>) {
// 开启事务
db.transaction(() => {
// SQL
let sql = `INSERT INTO ${startMenuItemTableName} VALUES(?, ?, ?, ?)`;
// 添加到缓存表中
for (let i = 0; i < list.length; i++) {
db.prepare(sql).run(
i + 1,
list[i].name,
JSON.stringify(list[i].data),
i + 1
);
}
})();
}
export {
systenItemTableName,
startMenuItemTableName,
initSystemItem,
initStartMenuItemTable,
list,
deleteAll,
batchAdd,
};

View File

@ -0,0 +1,60 @@
import { release } from "node:os";
import {
batchAdd,
deleteAll,
initStartMenuItemTable,
initSystemItem,
list,
startMenuItemTableName,
systenItemTableName,
} from "./data";
import { CommonItem } from "../../../../types/item";
import { fork } from "../../../commons/utilityProcessUtils";
import { sendToWebContent } from "../../commons";
/**
*
*/
function getSystemItemList() {
initSystemItem();
return list(systenItemTableName);
}
/**
*
*/
function getStartMenuItemList() {
// 初始化表
initStartMenuItemTable();
// 查询缓存
let cacheList = list(startMenuItemTableName);
// 清空表
deleteAll(startMenuItemTableName);
// 子进程
fork("getStartMenuItemList", cacheList, (resultList: Array<CommonItem>) => {
// 添加缓存
batchAdd(resultList);
// 发送消息到页面
sendToWebContent("itemAddEditWindow", "onGetStartMenuItemList", resultList);
});
}
/**
* APPX项目
*/
function getAppxItemList() {
// 大于win10才有APPX
let releaseArr = release().split(".");
if (Number(releaseArr[0]) >= 10) {
// 子进程
fork("getAppxItemList", {}, (resultList: Array<CommonItem>) => {
// 发送消息到页面
sendToWebContent("itemAddEditWindow", "onGetAppxItemList", resultList);
});
} else {
// 发送消息到页面
sendToWebContent("itemAddEditWindow", "onGetAppxItemList", []);
}
}
export { getSystemItemList, getStartMenuItemList, getAppxItemList };

427
electron/main/item/data.ts Normal file
View File

@ -0,0 +1,427 @@
import { Item, ItemData } from "../../../types/item";
import { newItem, newItemData } from "../../../commons/utils/common";
import { list as selectClassificationList } from "../classification/data";
import { getDataSqlite3 } from "../../commons/betterSqlite3";
// 获取数据库
let db = getDataSqlite3();
// 项目表名
let itemTableName = "item";
// 查询字段
let selectColumn =
"id, classification_id classificationId, name, type, data, shortcut_key shortcutKey, global_shortcut_key globalShortcutKey, `order`";
let simpleSelectColumn =
"id, classification_id classificationId, name, type, shortcut_key shortcutKey, global_shortcut_key globalShortcutKey, `order`";
/**
*
*/
function getItem(row: any): Item {
return newItem({
id: row.id,
classificationId: row.classificationId,
name: row.name,
type: row.type,
data: newItemData(row.data ? JSON.parse(row.data) : {}),
shortcutKey: row.shortcutKey,
globalShortcutKey: row.globalShortcutKey === 1,
order: row.order,
});
}
/**
*
*/
function init() {
// sql
let sql = `CREATE TABLE IF NOT EXISTS ${itemTableName} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
classification_id INTEGER NOT NULL,
name TEXT NOT NULL,
type INTEGER NOT NULL,
data TEXT NOT NULL,
shortcut_key TEXT,
global_shortcut_key INTEGER NOT NULL,
\`order\` INTEGER NOT NULL)`;
// 运行
db.exec(sql);
}
/**
*
* @param item
* @param reuseId
*/
function add(item: Item, reuseId: boolean = false) {
// 获取序号
let newOrder = getMaxOrder(item.classificationId) + 1;
// SQL
let sql = `INSERT INTO ${itemTableName}
(classification_id, name, type, data, shortcut_key, global_shortcut_key, \`order\`)
VALUES (?, ?, ?, ?, ?, ?, ?)`;
// 参数
let params = [
item.classificationId,
item.name,
item.type,
JSON.stringify(item.data),
item.shortcutKey,
item.globalShortcutKey ? 1 : 0,
newOrder,
];
// 重复使用ID
if (reuseId && item.id) {
sql = `INSERT INTO ${itemTableName}
(id, classification_id, name, type, data, shortcut_key, global_shortcut_key, \`order\`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
params.unshift(item.id);
}
// 运行
let id = db.prepare(sql).run(params).lastInsertRowid as number;
if (id) {
item.id = id;
item.order = newOrder;
return item;
}
return null;
}
/**
*
* @param classificationId
* @param itemList
* @param reuseId
*/
function batchAdd(
classificationId: number,
itemList: Array<Item>,
reuseId: boolean = false
) {
// 返回信息
let resultList: Array<Item> = [];
// 事务
db.transaction(() => {
// 获取序号
let newOrder = getMaxOrder(classificationId) + 1;
// 循环添加
for (let item of itemList) {
// SQL
let sql = `INSERT INTO ${itemTableName}
(classification_id, name, type, data, shortcut_key, global_shortcut_key, \`order\`)
VALUES (?, ?, ?, ?, ?, ?, ?)`;
// 参数
let params = [
classificationId,
item.name,
item.type,
JSON.stringify(item.data),
item.shortcutKey,
item.globalShortcutKey ? 1 : 0,
newOrder,
];
// 重复使用ID
if (reuseId && item.id) {
sql = `INSERT INTO ${itemTableName}
(id, classification_id, name, type, data, shortcut_key, global_shortcut_key, \`order\`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`;
params.unshift(item.id);
}
// 运行
let id = db.prepare(sql).run(params).lastInsertRowid as number;
if (id) {
item.id = id;
item.order = newOrder;
resultList.push(item);
}
newOrder++;
}
})();
return resultList;
}
/**
*
* @param item
*/
function update(item: Item) {
// SQL
let sql = `UPDATE ${itemTableName}
SET name = ?,
data = ?,
shortcut_key = ?,
global_shortcut_key = ?
WHERE id = ?`;
// 运行
return (
db
.prepare(sql)
.run(
item.name,
JSON.stringify(item.data),
item.shortcutKey,
item.globalShortcutKey ? 1 : 0,
item.id
).changes > 0
);
}
/**
* ID
* @param oldClassificationId
* @param newlassificationId
*/
function updateClassificationId(
oldClassificationId: number,
newClassificationId: number
) {
// SQL
let sql = `UPDATE ${itemTableName}
SET classification_id = ?
WHERE classification_id = ?`;
// 运行
return (
db.prepare(sql).run(newClassificationId, oldClassificationId).changes > 0
);
}
/**
*
* @param id
* @param itemData
*/
function updateData(id: number, itemData: ItemData) {
// SQL
let sql = `UPDATE ${itemTableName}
SET data = ?
WHERE id = ?`;
// 运行
return db.prepare(sql).run(JSON.stringify(itemData), id).changes > 0;
}
/**
*
* @param classificationId
*/
function getMaxOrder(classificationId: number) {
// SQL
let sql = `SELECT MAX(\`order\`) \`order\` FROM ${itemTableName} WHERE classification_id = ?`;
// 运行
let row: any = db.prepare(sql).get(classificationId);
if (row && row.order) {
return row.order;
} else {
return 0;
}
}
/**
* ID查询
* @param id
*/
function selectById(id: number): Item | null {
// SQL
let sql = `SELECT ${selectColumn} FROM ${itemTableName} WHERE id = ?`;
// 运行
let row = db.prepare(sql).get(id);
// 返回
if (row) {
return getItem(row);
} else {
return null;
}
}
/**
*
* @param simple
* @param classificationId
*/
function list(simple: boolean = false, classificationId: number | null = null) {
// 参数
let params = [];
// sql
let sql = `SELECT ${
simple ? simpleSelectColumn : selectColumn
} FROM ${itemTableName}`;
if (classificationId) {
sql += " WHERE classification_id = ?";
params.push(classificationId);
}
sql += " ORDER BY `order` ASC";
// 查询
let list = db.prepare(sql).all(params);
// 返回
return list.map((row) => {
return getItem(row);
});
}
/**
* ID列表查询
* @param simple
* @param idList
*/
function selectByIdList(simple: boolean, idList: Array<number>) {
// 参数
let params = [];
// sql
let sql = `SELECT ${
simple ? simpleSelectColumn : selectColumn
} FROM ${itemTableName} WHERE id IN (`;
for (let i = 0; i < idList.length; i++) {
sql += "?";
if (i !== idList.length - 1) {
sql += ",";
}
params.push(idList[i]);
}
sql += ")";
// 查询
let list = db.prepare(sql).all(params);
// 转为Item
let itemList = list.map((row) => {
return getItem(row);
});
// 返回列表
let resultList: Array<Item> = [];
// 根据传入的参数排序
for (const id of idList) {
for (const item of itemList) {
if (id === item.id) {
resultList.push(item);
break;
}
}
}
return resultList;
}
/**
*
* @param id
*/
function del(id: number) {
// 查询数据
let item = selectById(id);
if (item) {
// SQL
let sql = `DELETE FROM ${itemTableName} WHERE id = ?`;
// 运行
let res = db.prepare(sql).run(id).changes > 0;
if (res) {
// 更新序号
reorder(item.classificationId);
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
*
* @param classificationId
*/
function deleteByClassificationId(classificationId: number) {
// SQL
let sql = `DELETE FROM ${itemTableName} WHERE classification_id = ?`;
// 运行
return db.prepare(sql).run(classificationId).changes > 0;
}
/**
*
* @param classification_id
*/
function reorder(classification_id: number) {
// 查询项目列表
let itemList = list(true, classification_id);
// 开启事务
db.transaction(() => {
// SQL
let sql = `UPDATE ${itemTableName} SET \`order\` = ? WHERE id = ?`;
// 更新序号
for (let i = 0; i < itemList.length; i++) {
db.prepare(sql).run(i + 1, itemList[i].id);
}
})();
}
/**
*
* @param fromIdList
* @param toClassificationId
* @param toId
*/
function updateOrder(
fromIdList: Array<number>,
toClassificationId: number,
newIndex: number | null
) {
// 查询来源项目
let fromItemList = selectByIdList(true, fromIdList);
if (fromItemList.length > 0) {
// 查询目标分类是否是父级分类,如果是父级分类的话,获取他下面的第一个子分类
let classificationList = selectClassificationList(toClassificationId);
if (classificationList.length > 0) {
toClassificationId = classificationList[0].id;
}
// 记录来源项目都是来源于哪些分类,需要重新排序
let fromClassificationIdList = [];
for (const item of fromItemList) {
if (
item.classificationId !== toClassificationId &&
!fromClassificationIdList.includes(item.classificationId)
) {
fromClassificationIdList.push(item.classificationId);
}
}
// 查询目标项目列表
let toItemList = list(true, toClassificationId);
// 来源项目分类可能和目标项目分类是一样的,首先先从目标项目列表去掉来源项目
for (const id of fromIdList) {
toItemList = toItemList.filter((item) => item.id !== id);
}
// 将来源项目插入到目标项目列表
if (newIndex !== null) {
toItemList.splice(newIndex, 0, ...fromItemList);
} else {
// 尾部追加
toItemList.push(...fromItemList);
}
// 开启事务
db.transaction(() => {
// SQL
let sql = `UPDATE ${itemTableName} SET \`order\` = ?, classification_id = ? WHERE id = ?`;
// 更新序号
for (let i = 0; i < toItemList.length; i++) {
db.prepare(sql).run(i + 1, toClassificationId, toItemList[i].id);
}
})();
// 重排序其来源分类项目列表
for (const id of fromClassificationIdList) {
reorder(id);
}
return true;
}
return false;
}
export {
init,
list,
add,
batchAdd,
update,
del,
selectById,
selectByIdList,
deleteByClassificationId,
updateOrder,
updateData,
updateClassificationId,
};

687
electron/main/item/index.ts Normal file
View File

@ -0,0 +1,687 @@
import { BrowserWindow, shell, dialog, app } from "electron";
import { join } from "node:path";
import { getAbsolutePath, getURLParams } from "../../commons/utils";
import { Item } from "../../../types/item";
import {
batchAdd,
deleteByClassificationId,
list,
selectById,
selectByIdList,
updateData,
updateOrder,
} from "./data";
import { accessSync, writeFile, statSync, readFileSync } from "node:fs";
import mime from "mime";
import {
deleteExtname,
getItemName,
isAbsolutePath,
} from "../../../commons/utils/common";
import { iconExts } from "../../commons/utils";
import { addAssociateFolderWatcher } from "../classification";
import {
closeWindow,
convertPath,
getMainBackgorunColor,
sendToWebContent,
} from "../commons/index";
import { fork } from "../../commons/utilityProcessUtils";
// 窗口
let itemAddEditWindow: BrowserWindow | null = null;
let itemNetworkIconWindow: BrowserWindow | null = null;
let itemSVGIconWindow: BrowserWindow | null = null;
/**
* /
* @param id
* @param classificationId
*/
async function createAddEditWindow(
id: number | null,
classificationId: number | null
) {
// 如果窗口存在先关闭窗口
closeWindow(itemAddEditWindow);
// 创建窗口
itemAddEditWindow = global.itemAddEditWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 500,
width: 600,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
// 参数
let params = new Map();
if (id) {
params.set("id", id);
}
if (classificationId) {
params.set("classificationId", classificationId);
}
if (process.env.VITE_DEV_SERVER_URL) {
itemAddEditWindow.loadURL(
process.env.VITE_DEV_SERVER_URL + "item/AddEdit" + getURLParams(params)
);
} else {
itemAddEditWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/item/AddEdit",
search: getURLParams(params),
});
}
itemAddEditWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
itemAddEditWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
itemAddEditWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
itemAddEditWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
*/
async function createNetworkIconWindow() {
// 如果窗口存在先关闭窗口
closeWindow(itemNetworkIconWindow);
// 创建窗口
itemNetworkIconWindow = global.itemNetworkIconWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.itemAddEditWindow,
height: 230,
width: 400,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
itemNetworkIconWindow.loadURL(
process.env.VITE_DEV_SERVER_URL + "item/NetworkIcon"
);
} else {
itemNetworkIconWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/item/NetworkIcon",
});
}
itemNetworkIconWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
itemNetworkIconWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
itemNetworkIconWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
itemNetworkIconWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
* SVG图标窗口
*/
async function createSVGIconWindow() {
// 如果窗口存在先关闭窗口
closeWindow(itemSVGIconWindow);
// 创建窗口
itemSVGIconWindow = global.itemSVGIconWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.itemAddEditWindow,
height: 230,
width: 400,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
itemSVGIconWindow.loadURL(process.env.VITE_DEV_SERVER_URL + "item/SVGIcon");
} else {
itemSVGIconWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/item/SVGIcon",
});
}
itemSVGIconWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
itemSVGIconWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
itemSVGIconWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
itemSVGIconWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
* @param idList
* @param toClassificationId
*/
function copy(idList: Array<number>, toClassificationId: number) {
// 返回列表
let resultList: Array<Item> = [];
// 查询项目
let itemList = selectByIdList(false, idList);
if (itemList.length > 0) {
// 清空打开信息
resultList.forEach((item) => clearOpenInfo(item));
// 批量添加
resultList = batchAdd(toClassificationId, itemList);
}
if (resultList.length > 0) {
// 通知前端
sendToWebContent("mainWindow", "onAddItem", {
itemList: resultList,
clear: false,
classificationId: null,
});
}
}
/**
* /
* @param idList
* @param toClassificationId
*/
function move(idList: Array<number>, toClassificationId: number) {
// 移动项目
let res = updateOrder(idList, toClassificationId, null);
if (res) {
// 通知前端
sendToWebContent("mainWindow", "onMoveItem", {
idList,
toClassificationId,
});
}
}
/**
*
* @param classificationId
* @param pathList
*/
function drop(classificationId: number, pathList: Array<string>) {
fork(
"getDropItemInfo",
{
classificationId,
pathList,
},
(resultList: Array<Item>) => {
// 添加项目
let itemList = batchAdd(classificationId, resultList);
// 发送消息到页面
sendToWebContent("mainWindow", "onAddItem", {
itemList,
clear: false,
classificationId: null,
});
}
);
}
/**
*
* @param type
* @param id
*/
function updateOpenInfo(type: string, id: number) {
// 查询项目
let curItem = selectById(id);
if (curItem) {
if (type === "main" || type === "search") {
// 记录打开信息
curItem.data.lastOpen = new Date().getTime();
// 记录打开次数
if (global.setting.item.openNumber) {
curItem.data.openNumber += 1;
}
} else if (type === "quickSearch") {
// 记录打开信息
curItem.data.quickSearchLastOpen = new Date().getTime();
// 记录打开次数
curItem.data.quickSearchOpenNumber += 1;
}
if (updateData(curItem.id, curItem.data)) {
sendToWebContent("mainWindow", "onUpdateOpenInfo", {
id: curItem.id,
openNumber: curItem.data.openNumber,
lastOpen: curItem.data.lastOpen,
quickSearchOpenNumber: curItem.data.quickSearchOpenNumber,
quickSearchLastOpen: curItem.data.quickSearchLastOpen,
type,
});
}
}
}
/**
*
* @param type
* @param operation
* @param item
*/
function run(
type: string,
operation: "open" | "runas" | "openFileLocation",
item: Item
) {
if (item.data) {
// 更新打开信息
updateOpenInfo(type, item.id);
// 判断类型
if (item.type === 2) {
// 网址
shell.openExternal(item.data.target);
} else if (item.type === 3 || item.type === 4) {
// 系统 或 appx
global.addon.systemItemExecute(item.data.target, item.data.params);
} else {
// 获取绝对路径
if (item.type === 0 || item.type === 1) {
// 获取路径
item.data.target = getAbsolutePath(item.data.target);
}
try {
// 判断文件或文件夹是否存在
accessSync(item.data.target);
// 存在
if (operation === "openFileLocation") {
// 打开文件所在位置
global.addon.openFileLocation(item.data.target);
} else {
// 运行
global.addon.shellExecute(
operation,
item.data.target,
item.data.params ?? "",
item.data.startLocation
);
}
} catch (e) {
let message: string | null = null;
if (item.type === 0 && operation !== "openFileLocation") {
message = global.language.notFoundFile;
} else {
message = global.language.notFoundFolder;
}
message += '"' + item.data.target + '"';
dialog.showMessageBox(global.mainWindow, {
title: "Dawn Launcher",
message: message,
buttons: [global.language.ok],
type: "error",
noLink: true,
});
}
}
}
}
/**
*
* @param idList
* @param type
*/
function convertTarget(idList: Array<number>, type: "Absolute" | "Relative") {
// 返回数据
let resultList = [];
// 查询数据
let itemList = selectByIdList(false, idList);
for (let item of itemList) {
// 是否是绝对路径
let isAbsolute = isAbsolutePath(item.data.target);
// 如果是绝对路径并且type是Relative就转为相对路径
// 如果不是绝对路径并且type是Absolute就转为绝对路径
if (
(isAbsolute && type === "Relative") ||
(!isAbsolute && type === "Absolute")
) {
// 转换路径
item.data.target = convertPath(item.data.target);
// 更新
updateData(item.id, item.data);
// push
resultList.push({
id: item.id,
target: item.data.target,
});
}
}
// 通知页面
sendToWebContent("mainWindow", "onConvertPath", resultList);
}
/**
*
* @param item
*/
function exportIcon(item: Item) {
if (item.data.icon || item.data.htmlIcon) {
// SVG代码图标
let svgCode = false;
// 拓展名
let extensionName: string | null = null;
// 内容
let content: string | null = null;
if (item.data.htmlIcon && item.data.htmlIcon.trim() !== "") {
// 保存为SVG图片
svgCode = true;
extensionName = "svg";
content = item.data.htmlIcon.trim();
} else if (item.data.icon && item.data.icon.trim() !== "") {
// 提取base64类型
let re = new RegExp("data:(?<ext>.*?);base64,.*");
let res = re.exec(item.data.icon);
if (res && res.groups) {
// 获取拓展名
extensionName = mime.getExtension(res.groups.ext);
if (item.data.icon.trim().split(",").length === 2) {
content = item.data.icon.trim().split(",")[1];
}
}
}
// 弹出文件对话框保存
if (extensionName && content) {
let path = dialog.showSaveDialogSync(global.mainWindow, {
defaultPath: "icon",
filters: [
{
name: extensionName,
extensions: [extensionName],
},
],
});
// 保存
if (path) {
if (svgCode) {
writeFile(path, content, function (err) {});
} else {
let dataBuffer = Buffer.from(content, "base64");
writeFile(path, dataBuffer, function (err) {});
}
}
}
}
}
/**
*
* @param idList
*/
function refreshIcon(idList: Array<number>) {
// 查询项目
let itemList = selectByIdList(false, idList);
// 过滤掉固定图标项目
itemList = itemList.filter((item) => !item.data.fixedIcon);
// 子进程
fork(
"refreshItemIcon",
itemList,
(resultList: Array<{ id: number; icon: string }>) => {
// 项目列表
let itemList: Array<{ id: number; icon: string }> = [];
// 更新项目图标
for (const data of resultList) {
let item = selectById(data.id);
if (item) {
item.data.icon = data.icon;
item.data.htmlIcon = null;
let res = updateData(item.id, item.data);
if (res) {
itemList.push({
id: item.id,
icon: item.data.icon,
});
}
}
}
// 发送消息到页面
sendToWebContent("mainWindow", "onRefreshItemIcon", itemList);
}
);
}
/**
*
* @param item
*/
async function createShortcut(item: Item) {
let target = item.data.target;
if (item.type === 0 || item.type === 1) {
// 获取绝对路径
target = getAbsolutePath(target);
}
// 保存路径
let savePath =
app.getPath("desktop") +
"\\" +
deleteExtname(getItemName(item.name)) +
".lnk";
shell.writeShortcutLink(savePath, "create", {
target: target,
});
}
/**
*
* @param classificationId
* @param dir
* @param hiddenItems
* @param listen
* @param clear
*/
function getDirectoryList(
classificationId: number,
dir: string | null,
hiddenItems: string | null,
listen: boolean,
clear: boolean
) {
// 校验目录
if (!dir || dir.trim() === "") {
return;
}
// 校验是否存在,校验是否是文件夹
try {
let stats = statSync(dir);
if (!stats.isDirectory()) {
return;
}
} catch (e) {
return;
}
// 查询旧数据
let oldList = list(false, classificationId);
// 子进程
fork(
"getDirectoryItemList",
{
classificationId,
dir,
hiddenItems,
oldList,
},
(resultList: Array<Item>) => {
if (clear) {
// 删除旧数据
deleteByClassificationId(classificationId);
}
// 添加项目
let itemList = batchAdd(classificationId, resultList, true);
if (listen) {
// 创建关联文件夹监听
addAssociateFolderWatcher(classificationId, dir, hiddenItems);
}
if (global.mainWindow && !global.mainWindow.isDestroyed()) {
// 发送消息到页面
sendToWebContent("mainWindow", "onAddItem", {
itemList,
clear,
classificationId,
});
}
}
);
}
/**
*
*/
function getClipboardImageFile() {
// 获取文件列表
let fileList: Array<string> = global.addon.getClipboardFileList();
// 多个文件返回空,单个文件返回文件路径
if (fileList.length === 1) {
let filePath = fileList[0];
// 获取后缀
let ext = mime.getExtension(mime.getType(filePath));
if (iconExts.includes(ext)) {
return filePath;
}
}
return null;
}
/**
*
* @param id
*/
function pasteIcon(id: number | null) {
if (id) {
// 查询项目
let item = selectById(id);
if (item) {
// 图标
let icon = null;
// 获取剪切板图片文件
let imageFile = getClipboardImageFile();
if (imageFile) {
try {
// 读取文件
let buffer = readFileSync(imageFile);
// 图标
icon =
"data:" +
mime.getType(imageFile) +
";base64," +
buffer.toString("base64");
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
} else {
let bitmap = global.addon.getClipboardBitmapBase64();
if (bitmap) {
icon = bitmap;
}
}
if (icon) {
item.data.icon = icon;
// 更新
updateData(item.id, item.data);
// 通知页面刷新图标
sendToWebContent("mainWindow", "onRefreshItemIcon", [
{ id, icon: item.data.icon },
]);
}
}
}
}
/**
*
*/
function checkInvalid() {
// 查询项目列表
let itemList = list();
// 去掉图标
for (const item of itemList) {
item.data.icon = null;
}
// 子进程
fork("checkInvalidItem", itemList, (resultList: Array<number>) => {
// 发送消息到页面
sendToWebContent("mainWindow", "onCheckInvalidItem", resultList);
});
}
/**
*
* @param item
*/
function clearOpenInfo(item: Item) {
item.data.openNumber = 0;
item.data.lastOpen = 0;
item.data.quickSearchOpenNumber = 0;
item.data.quickSearchLastOpen = 0;
}
export {
createAddEditWindow,
createNetworkIconWindow,
createSVGIconWindow,
copy,
move,
drop,
updateOpenInfo,
run,
convertTarget,
exportIcon,
refreshIcon,
createShortcut,
getDirectoryList,
getClipboardImageFile,
pasteIcon,
checkInvalid,
};

View File

@ -0,0 +1,680 @@
import {
Menu,
MenuItem,
dialog,
ipcMain,
clipboard,
MenuItemConstructorOptions,
} from "electron";
import { getAbsolutePath } from "../../commons/utils";
import {
convertTarget,
createAddEditWindow,
createNetworkIconWindow,
createSVGIconWindow,
createShortcut,
drop,
exportIcon,
refreshIcon,
run,
copy,
move,
getClipboardImageFile,
pasteIcon,
updateOpenInfo,
} from ".";
import {
getAppxItemList,
getStartMenuItemList,
getSystemItemList,
} from "./commons";
import { add, del, list, selectById, update, updateOrder } from "./data";
import { Item } from "../../../types/item";
import { getFileExtname, isAbsolutePath } from "../../../commons/utils/common";
import {
list as selectClassificationList,
selectById as selectClassificationById,
hasChildClassification,
} from "../classification/data";
import { getWindowInScreen } from "../main/index";
import {
getItemLayoutMenu,
getItemSortMenu,
getItemColumnNumber,
getItemIconSize,
getItemShowOnly,
} from "../classification";
import { join } from "node:path";
import { setShortcutKey } from "../setting";
import {
closeWindow,
openAfterHideWindow,
sendToWebContent,
} from "../commons/index";
/**
* /
* @param idList
* @param type
* @returns
*/
function getCopyMoveMenuItems(
idList: Array<number>,
type: "MoveItem" | "CopyItem"
) {
// 菜单
let menuItems: Array<MenuItemConstructorOptions> = [];
// 查询分类
let classificationList = selectClassificationList(null);
for (const parent of classificationList) {
if (parent.parentId || parent.type !== 0) {
continue;
}
let submenus: Array<MenuItemConstructorOptions> = [];
// 子分类
for (const child of classificationList) {
if (parent.id === child.parentId && child.type === 0) {
submenus.push({
label: child.name,
click: () => {
if (type === "CopyItem") {
copy(idList, child.id);
} else {
move(idList, child.id);
}
},
});
}
}
if (submenus.length > 0) {
menuItems.push({
label: parent.name,
submenu: submenus,
});
} else {
menuItems.push({
label: parent.name,
click: () => {
if (type === "CopyItem") {
copy(idList, parent.id);
} else {
move(idList, parent.id);
}
},
});
}
}
return menuItems;
}
export default function () {
// 显示新增/修改窗口
ipcMain.on("showItemAddEditWindow", () => {
if (global.itemAddEditWindow) {
global.itemAddEditWindow.show();
}
});
// 关闭新增/修改窗口
ipcMain.on("closeItemAddEditWindow", () => {
closeWindow(global.itemAddEditWindow);
});
// 获取项目列表
ipcMain.on("getItemList", (event) => {
event.returnValue = list();
});
// 获取简单项目列表
ipcMain.on("getSimpleItemList", (event) => {
event.returnValue = list(true);
});
// 根据ID查询项目
ipcMain.on("getItemById", (event, args) => {
event.returnValue = selectById(args.id);
});
// 添加项目
ipcMain.on("addItem", (event, args) => {
let item = add(args);
setShortcutKey();
event.returnValue = item;
});
// 更新项目
ipcMain.on("updateItem", (event, args) => {
let res = update(args);
setShortcutKey();
event.returnValue = res;
});
// 项目排序
ipcMain.on("updateItemOrder", (event, args) => {
event.returnValue = updateOrder(
args.fromIdList,
args.toClassificationId,
args.newIndex
);
});
// 右键菜单
ipcMain.on("showItemRightMenu", (event, args) => {
// 类型 main:主界面 search:搜索模块
let type: string = args.type;
// 项目
let item: Item | null = args.item;
// 锁定/解锁项目
let lockItem: boolean = type === "main" ? args.lockItem : false;
// 批量操作
let batchOperation: boolean = type === "main" ? args.batchOperation : false;
// 批量操作ID列表
let batchSelectedIdList: Array<number> =
type === "main" ? args.batchSelectedIdList : [];
// 分类ID
let classificationId: number =
type === "main" ? args.classificationId : item.classificationId;
// 查询分类信息
let classification = selectClassificationById(classificationId);
if (!classification) {
return;
}
// 菜单
let menuList: Array<MenuItem> = [];
// 组装菜单
if (!batchOperation) {
if (item) {
// 后缀
let ext: string | null = null;
// 排除
if (item.type === 0) {
ext = getFileExtname(item.data.target);
}
// "打开"菜单
let openMenu = false;
if (item.type === 0 && ext && (ext === "exe" || ext === "bat")) {
menuList.push(
new MenuItem({
label: global.language.runAsAdministrator,
click: () => {
// 运行
run(type, "runas", item);
openAfterHideWindow(type);
},
})
);
openMenu = true;
}
if (item.type === 0 || item.type === 1) {
menuList.push(
new MenuItem({
label: global.language.openFileLocation,
click: () => {
// 运行
run(type, "openFileLocation", item);
openAfterHideWindow(type);
},
})
);
openMenu = true;
}
if (item.type === 3 && item.data.target === "shell:RecycleBinFolder") {
menuList.push(
new MenuItem({
label: global.language.emptyRecycleBin,
click: () => {
global.addon.emptyRecycleBin(
global.mainWindow.getNativeWindowHandle().readInt32LE(0)
);
},
})
);
openMenu = true;
}
if (
item.type === 0 ||
item.type === 1 ||
(item.type === 3 && item.data.target.indexOf("shell:") >= 0)
) {
menuList.push(
new MenuItem({
label: global.language.explorerMenu,
click: () => {
// 获取当前窗口所在屏幕
let screen = getWindowInScreen();
let scaleFactor = 1;
if (screen && screen.length > 0) {
scaleFactor = screen[0].scaleFactor;
}
// 弹出资源管理器菜单
sendToWebContent(
type === "quickSearch" ? "quickSearchWindow" : "mainWindow",
"onItemExplorerMenu",
{
type: type,
id: item.id,
}
);
// 禁用鼠标HOOK
global.addon.disableMouseHook();
// 弹出资源管理器菜单
global.addon.explorerContextMenu(
global.mainWindow.getNativeWindowHandle().readInt32LE(0),
item.type === 0 || item.type === 1
? getAbsolutePath(item.data.target)
: item.data.target,
args.x * scaleFactor,
args.y * scaleFactor
);
// 开启鼠标HOOK
global.addon.enableMouseHook();
sendToWebContent(
type === "quickSearch" ? "quickSearchWindow" : "mainWindow",
"onItemRightMenuClose",
{}
);
},
})
);
openMenu = true;
}
if (openMenu) {
menuList.push(new MenuItem({ type: "separator" }));
}
// "路径"菜单
let pathMenu = false;
if (item.type === 0 || item.type === 1 || item.type === 2) {
menuList.push(
new MenuItem({
label: global.language.copyFullPath,
click: () => {
clipboard.writeText(
item.type === 0 || item.type === 1
? getAbsolutePath(item.data.target)
: item.data.target
);
},
})
);
pathMenu = true;
}
if (
type === "main" &&
(item.type === 0 || item.type === 1) &&
classification.type === 0
) {
menuList.push(
new MenuItem({
label: isAbsolutePath(item.data.target)
? global.language.convertRelativePath
: global.language.convertAbsolutePath,
click: () => {
convertTarget(
[item.id],
isAbsolutePath(item.data.target) ? "Relative" : "Absolute"
);
},
})
);
pathMenu = true;
}
if (item.type === 0 || item.type === 1 || item.type === 2) {
menuList.push(
new MenuItem({
label: global.language.createShortcut,
click: () => {
createShortcut(item);
},
})
);
pathMenu = true;
}
if (pathMenu) {
menuList.push(new MenuItem({ type: "separator" }));
}
if (type === "main") {
// "图标"菜单
let existPasteIcon = false;
// 获取剪切板图片文件
let imageFile = getClipboardImageFile();
if (imageFile) {
existPasteIcon = true;
} else {
// 判断剪切板中的BITMAP是否存在
let hasBitmap = global.addon.clipboardHasBitmap();
if (hasBitmap) {
existPasteIcon = true;
}
}
if (existPasteIcon) {
menuList.push(
new MenuItem({
label: global.language.pasteIcon,
click: () => {
pasteIcon(item.id);
},
})
);
}
}
menuList.push(
new MenuItem({
label: global.language.exportIcon,
click: () => {
exportIcon(item);
},
})
);
if (
type === "main" &&
(item.type === 0 || item.type === 1) &&
!item.data.fixedIcon
) {
menuList.push(
new MenuItem({
label: global.language.refreshIcon,
click: () => {
refreshIcon([item.id]);
},
})
);
}
if (type === "main" && classification.type === 0) {
menuList.push(new MenuItem({ type: "separator" }));
menuList.push(
new MenuItem({
label: global.language.moveTo,
submenu: getCopyMoveMenuItems([item.id], "MoveItem"),
})
);
menuList.push(
new MenuItem({
label: global.language.copyTo,
submenu: getCopyMoveMenuItems([item.id], "CopyItem"),
})
);
menuList.push(new MenuItem({ type: "separator" }));
// 项目通用
menuList.push(
new MenuItem({
label: global.language.edit,
click: () => {
// 创建窗口
createAddEditWindow(item.id, null);
},
}),
new MenuItem({
label: global.language.delete,
click: () => {
let res = dialog.showMessageBoxSync(global.mainWindow, {
message: global.language.deleteItemPrompt,
buttons: [global.language.ok, global.language.cancel],
type: "question",
noLink: true,
cancelId: 1,
});
if (res === 0) {
// 删除数据
del(item.id);
// 快捷键
setShortcutKey();
// 通知前端删除数据
sendToWebContent("mainWindow", "onDeleteItem", [item.id]);
}
},
})
);
}
} else {
// 尝试查询分类下有没有子分类
let classificationList = selectClassificationList(classificationId);
// 添加项目选项
if (classificationList && classificationList.length > 0) {
let submenus = [];
for (const classification of classificationList) {
if (classification.type === 0) {
submenus.push(
new MenuItem({
label: classification.name,
click: () => {
// 创建窗口
createAddEditWindow(null, classification.id);
},
})
);
}
}
if (submenus.length > 0) {
menuList.push(
new MenuItem({
label: global.language.newItem,
submenu: submenus,
})
);
}
} else {
if (classification.type === 0) {
menuList.push(
new MenuItem({
label: global.language.newItem,
click: () => {
// 创建窗口
createAddEditWindow(null, classificationId);
},
})
);
}
}
// 分割线
menuList.push(new MenuItem({ type: "separator" }));
if (classification.type !== 2) {
// 排序
menuList.push(getItemSortMenu(classification));
}
// 布局
menuList.push(getItemLayoutMenu(classification));
// 列数
if (
!hasChildClassification(classificationId) &&
(classification.data.itemLayout === "list" ||
(global.setting.item.layout === "list" &&
classification.data.itemLayout === "default"))
) {
// 只有子级分类或没有子级分类的父级分类并且布局是列表的才显示列数
menuList.push(getItemColumnNumber(classification));
}
// 图标
menuList.push(getItemIconSize(classification));
// 显示
menuList.push(getItemShowOnly(classification));
// 只有普通分类可以锁定/解锁分类
if (classification.type === 0) {
menuList.push(
new MenuItem({ type: "separator" }),
new MenuItem({
label: !lockItem
? global.language.lockItem
: global.language.unlockItem,
click: () => {
sendToWebContent("mainWindow", "onLockItem", {});
},
})
);
}
}
} else {
if (classification.type === 0) {
menuList.push(
new MenuItem({
label: global.language.selectAll,
click: () => {
sendToWebContent(
"mainWindow",
"onItembatchOperationSelectAll",
{}
);
},
}),
new MenuItem({ type: "separator" }),
new MenuItem({
label: global.language.batchMoveTo,
submenu: getCopyMoveMenuItems(batchSelectedIdList, "MoveItem"),
}),
new MenuItem({
label: global.language.batchCopyTo,
submenu: getCopyMoveMenuItems(batchSelectedIdList, "CopyItem"),
}),
new MenuItem({ type: "separator" }),
new MenuItem({
label: global.language.batchConvertRelativePath,
click: () => {
convertTarget(batchSelectedIdList, "Relative");
},
}),
new MenuItem({
label: global.language.batchConvertAbsolutePath,
click: () => {
convertTarget(batchSelectedIdList, "Absolute");
},
}),
new MenuItem({ type: "separator" }),
new MenuItem({
label: global.language.batchRefreshIcon,
click: () => {
refreshIcon(batchSelectedIdList);
},
}),
new MenuItem({ type: "separator" }),
new MenuItem({
label: global.language.batchDelete,
click: () => {
let res = dialog.showMessageBoxSync(global.mainWindow, {
message: global.language.batchDeletePrompt,
buttons: [global.language.ok, global.language.cancel],
type: "question",
noLink: true,
cancelId: 1,
});
if (res === 0) {
for (const id of batchSelectedIdList) {
// 删除数据
del(id);
// 快捷键
setShortcutKey();
}
// 通知前端删除数据
sendToWebContent(
"mainWindow",
"onDeleteItem",
batchSelectedIdList
);
}
},
})
);
}
}
// 非锁定项目下可以批量操作
if (type === "main" && !lockItem && classification.type === 0) {
menuList.push(
new MenuItem({ type: "separator" }),
new MenuItem({
label: !batchOperation
? global.language.batchOperation
: global.language.cancelBatchOperation,
click: () => {
sendToWebContent(
"mainWindow",
"onItemBatchOperation",
!batchOperation
);
},
})
);
}
if (menuList.length > 0) {
// 载入菜单
let menu = Menu.buildFromTemplate(menuList);
// 菜单显示
menu.on("menu-will-show", () => {
global.itemRightMenu = true;
});
// 菜单关闭
menu.on("menu-will-close", () => {
global.itemRightMenu = false;
sendToWebContent(
type === "quickSearch" ? "quickSearchWindow" : "mainWindow",
"onItemRightMenuClose",
[]
);
});
// 显示
menu.popup();
}
});
// 创建网络图标窗口
ipcMain.on("createItemNetworkIconWindow", () => {
createNetworkIconWindow();
});
// 显示网络图标窗口
ipcMain.on("showItemNetworkIconWindow", () => {
if (global.itemNetworkIconWindow) {
global.itemNetworkIconWindow.show();
}
});
// 关闭网络图标窗口
ipcMain.on("closeItemNetworkIconWindow", () => {
closeWindow(global.itemNetworkIconWindow);
});
// 创建SVG图标窗口
ipcMain.on("createItemSVGIconWindow", () => {
createSVGIconWindow();
});
// 显示SVG图标窗口
ipcMain.on("showItemSVGIconWindow", () => {
if (global.itemSVGIconWindow) {
global.itemSVGIconWindow.show();
}
});
// 关闭SVG图标窗口
ipcMain.on("closeItemSVGIconWindow", () => {
closeWindow(global.itemSVGIconWindow);
});
// 获取系统项目
ipcMain.on("getSystemItemList", (event, args) => {
let res = getSystemItemList();
sendToWebContent("itemAddEditWindow", "onGetSystemItemList", res);
});
// 获取开始菜单项目
ipcMain.on("getStartMenuItemList", (event, args) => {
getStartMenuItemList();
});
// 获取APPX项目
ipcMain.on("getAppxItemList", (event, args) => {
getAppxItemList();
});
// 拖入项目
ipcMain.on("dropItem", (event, args) => {
drop(args.classificationId, args.pathList);
});
// 运行项目
ipcMain.on("runItem", (event, args) => {
run(args.type, args.operation, args.item);
});
// 项目拖出
ipcMain.on("itemDragOut", (event, args) => {
let item: Item = args;
try {
// 网站和系统不能拖出
if (item.type === 2 || item.type === 3) {
// 取消拖出状态
sendToWebContent("mainWindow", "onItemCancelDragOut", {});
return;
}
let icon = join(process.env.VITE_PUBLIC, "drag-and-drop.png");
event.sender.startDrag({
file: getAbsolutePath(item.data.target),
icon: icon,
});
} finally {
// 取消拖出状态
sendToWebContent("mainWindow", "onItemCancelDragOut", {});
}
});
// 更新打开信息
ipcMain.on("updateItemOpenInfo", (event, args) => {
updateOpenInfo(args.type, args.id);
});
}

701
electron/main/main/index.ts Normal file
View File

@ -0,0 +1,701 @@
import {
BrowserWindow,
Display,
Menu,
Tray,
app,
screen,
shell,
} from "electron";
import { createSettingWindow } from "../setting";
import { join } from "node:path";
import cacheData from "../commons/cacheData";
import { getMainBackgorunColor, sendToWebContent } from "../commons";
// 窗口
let mainWindow: BrowserWindow | null = null;
/**
*
*/
function createMainWindow() {
// 如果窗口存在先关闭窗口
if (mainWindow && !mainWindow.isDestroyed() && mainWindow.isVisible()) {
mainWindow.close();
mainWindow = null;
global.mainWindow = null;
}
// 创建窗口
mainWindow = global.mainWindow = new BrowserWindow({
title: "Dawn Launcher",
width: 800,
height: 600,
type: "toolbar",
frame: false,
show: false,
maximizable: false,
minimizable: false,
fullscreenable: false,
transparent: global.setting.appearance.transparency < 1,
skipTaskbar: true,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
webPreferences: {
spellcheck: false,
backgroundThrottling: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(join(process.env.DIST, "index.html"));
}
// 加载完毕
mainWindow.webContents.on("did-finish-load", () => {
// 恢复上一次的位置
let bounds = cacheData.cacheStore.get("mainWindowBounds");
if (bounds) {
mainWindow.setBounds(bounds);
}
// 永远居中不可移动
if (global.setting.general.alwaysCenter) {
mainWindow.setMovable(false);
} else {
mainWindow.setMovable(!global.setting.general.fixedPosition);
}
// 永远置顶
if (global.setting.general.alwaysTop) {
mainWindow.setAlwaysOnTop(true, "screen-saver");
}
// 锁定尺寸
if (!global.setting.general.lockSize) {
mainWindow.setResizable(!global.setting.general.lockSize);
}
// 托盘
createTray(!global.setting.general.hideTray);
// 永远居中
alwaysCenter();
// 判断窗口位置
let displays = getWindowInScreen();
if (displays.length === 0) {
// 代表窗口的位置不再任一屏幕内,将窗口位置移动到主窗口
mainWindow.center();
}
// 边缘吸附
edgeAdsorb(null);
});
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 显示窗口
mainWindow.on("show", () => {
// 显示窗口时将输入法切换为英文模式
if (global.setting.general.switchEnglish) {
global.addon.switchEnglish(
mainWindow.getNativeWindowHandle().readInt32LE(0)
);
}
// 边缘吸附
edgeAdsorb(null);
});
// 失去焦点
mainWindow.on("blur", () => {
if (global.setting.general.edgeAutoHide && global.blurHide) {
let scaleFactor = screen.getPrimaryDisplay().scaleFactor;
autoHide(
screen.getCursorScreenPoint().x * scaleFactor,
screen.getCursorScreenPoint().y * scaleFactor,
0,
false
);
}
if (
mainWindow.isVisible() &&
global.setting.general.hideLoseFocus &&
!global.setting.general.alwaysTop
) {
// 如果当前还有打开的子窗口就不执行失去焦点隐藏
if (mainWindow.getChildWindows().length === 0) {
hideMainWindow();
}
}
});
// 窗口移动完毕
mainWindow.on("moved", () => {
// 边缘吸附
edgeAdsorb(null);
// 永远居中
alwaysCenter();
// 记录位置
cacheData.cacheStore.set("mainWindowBounds", mainWindow.getBounds());
});
// 改变窗口大小完毕
mainWindow.on("resized", () => {
// 边缘吸附
edgeAdsorb(null);
// 永远居中
alwaysCenter();
// 记录位置
cacheData.cacheStore.set("mainWindowBounds", mainWindow.getBounds());
});
// 窗口隐藏事件
mainWindow.on("hide", () => {
// 收起子分类
if (global.setting.classification.hideWindowCollapseSubClassification) {
sendToWebContent("mainWindow", "onCollapseSubClassification", {});
}
});
// 创建鼠标hook
addon.createMouseHook((...args: any[]) => {
let res = JSON.parse(args[1]);
let event: string = res.event;
let x: number = res.x;
let y: number = res.y;
// 1左键 2右键 3滚轮
let button: number = res.button;
if (event === "mousemove") {
// 鼠标移动
if (!global.blurHide) {
// 停靠在桌面边缘时自动隐藏
autoHide(x, y, 40, true);
}
} else if (event === "mousedown") {
// 鼠标按下
} else if (event === "mouseup") {
// 鼠标抬起
if (button === 3) {
// 中间单击
// 显示隐藏窗口
showHideMouseWheelClick();
}
// 双击任务栏
doubleClickTaskbar(button);
}
});
// 禁用标题栏右键
mainWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
mainWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
mainWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
* @param blurHide
* @param selectedClassificationId
*/
function showMainWindowBefore(
blurHide: boolean,
selectedClassificationId: number | null = null
) {
// 向主窗口发送通知
sendToWebContent("mainWindow", "onShowMainWindowBefore", {
blurHide,
selectedClassificationId,
});
}
/**
*
* @param blurHide
*/
function showMainWindow(blurHide: boolean) {
// flag
let flag = true;
// 是否开启勿扰模式
if (global.setting.general.notDisturb) {
if (global.addon.isFullscreen()) {
flag = false;
}
}
// 显示时跟随鼠标位置
showFollowMousePosition();
if (flag) {
global.mainWindow.show();
// 如果没有设置置顶时,显示窗口先置顶一下,再取消
if (!global.mainWindow.isAlwaysOnTop) {
mainWindow.setAlwaysOnTop(true, "screen-saver");
mainWindow.setAlwaysOnTop(false);
}
global.mainWindow.focus();
global.blurHide = blurHide;
if (blurHide) {
global.blurHide = true;
} else {
global.blurHide = false;
}
}
}
/**
*
*/
function hideMainWindow() {
if (global.mainWindow.isVisible()) {
global.mainWindow.hide();
global.blurHide = false;
}
}
/**
*
*/
function createTray(show: boolean) {
if (show) {
// 销毁托盘
if (global.tray && !global.tray.isDestroyed()) {
global.tray.destroy();
global.tray = null;
}
// 创建托盘
global.tray = new Tray(join(process.env.VITE_PUBLIC, "tray.ico"));
// 菜单
let contextMenu = Menu.buildFromTemplate([
{
label: global.language.displayMainWindow,
click: function () {
showMainWindowBefore(true);
},
},
{
label: global.language.settings,
click: function () {
createSettingWindow();
},
},
{
label: global.language.exit,
click: function () {
app.quit();
},
},
]);
global.tray.setToolTip("Dawn Launcher");
global.tray.setContextMenu(contextMenu);
// 点击托盘
global.tray.on("click", () => {
showMainWindowBefore(true);
});
} else {
// 销毁托盘
if (global.tray && !global.tray.isDestroyed()) {
global.tray.destroy();
global.tray = null;
}
}
}
/**
*
*/
function getWindowInScreen() {
let inDisplays: Array<Display> = [];
let displays = screen.getAllDisplays();
let bounds = global.mainWindow.getBounds();
for (let display of displays) {
let workArea = display.workArea;
if (
((workArea.x <= bounds.x && workArea.x + workArea.width >= bounds.x) ||
(workArea.x <= bounds.x + bounds.width &&
workArea.x + workArea.width >= bounds.x + bounds.width)) &&
((workArea.y <= bounds.y && workArea.y + workArea.height >= bounds.y) ||
(workArea.y <= bounds.y + bounds.height &&
workArea.y + workArea.height >= bounds.y + bounds.height))
) {
inDisplays.push(display);
}
}
return inDisplays;
}
/**
*
* @param display
* @returns
*/
function edgeAdsorb(display: Display | null) {
// 如果勾选停靠在桌面边缘时自动隐藏,放行
if (
global.mainWindow.isDestroyed() ||
(!global.setting.general.edgeAdsorb && !global.setting.general.edgeAutoHide)
) {
return;
}
try {
// 清空方向
global.mainWindowDirection = null;
// 屏幕
let displays = display ? [display] : getWindowInScreen();
if (displays.length > 1 || displays.length === 0) {
return;
}
// 获取屏幕工作区域
let workArea = displays[0].workArea;
// 窗口位置信息
let bounds = global.mainWindow.getBounds();
if (bounds.x + bounds.width >= workArea.x + workArea.width) {
// 右侧
global.mainWindow.setBounds({
x: workArea.x + workArea.width - bounds.width,
});
global.mainWindowDirection = "right";
global.blurHide = false;
} else if (bounds.x <= workArea.x) {
// 左侧
global.mainWindow.setBounds({ x: workArea.x });
global.mainWindowDirection = "left";
global.blurHide = false;
}
if (bounds.y + bounds.height >= workArea.y + workArea.height) {
// 底部
global.mainWindow.setBounds({
y: workArea.y + workArea.height - bounds.height,
});
global.mainWindowDirection = "bottom";
global.blurHide = false;
} else if (bounds.y <= workArea.y) {
// 顶部
global.mainWindow.setBounds({ y: workArea.y });
global.mainWindowDirection = "top";
global.blurHide = false;
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
global.mainWindow.setBounds({ x: 1, y: 1 });
}
}
/**
*
*/
function showFollowMousePosition() {
// 当永远居中、固定位置勾选后不能使用显示时跟随鼠标位置
if (
!global.setting.general.alwaysCenter &&
!global.setting.general.fixedPosition &&
global.setting.general.showFollowMousePosition
) {
// 获取鼠标位置
let point = screen.getCursorScreenPoint();
// 窗口位置信息
let bounds = global.mainWindow.getBounds();
let x = Math.round(bounds.width / 2);
let y = Math.round(bounds.height / 2);
// 设置窗口位置
global.mainWindow.setPosition(point.x - x, point.y - y);
for (let i = 0; i < 10; i++) {
global.mainWindow.setSize(bounds.width, bounds.height);
}
// 获取当前鼠标所在屏幕
let display = screen.getDisplayNearestPoint(point);
// 边缘吸附
edgeAdsorb(display);
}
}
/**
* /
*/
function showHideMouseWheelClick() {
if (global.setting.general.showHideMouseWheelClick) {
if (global.mainWindow.isVisible()) {
hideMainWindow();
} else {
showMainWindowBefore(true);
}
}
}
/**
*
*/
function alwaysCenter() {
if (global.setting.general.alwaysCenter) {
mainWindow.center();
}
}
/**
*
* @param x
* @param y
* @param size
* @param timer /
* @returns
*/
function autoHide(x: number, y: number, size: number, timer: boolean) {
if (global.mainWindow.isDestroyed() || !global.setting.general.edgeAutoHide) {
return;
}
// 当有子窗口时不自动隐藏
if (mainWindow.getChildWindows().length > 0) {
return;
}
try {
// 屏幕
let displays = getWindowInScreen();
if (displays.length > 1 || displays.length === 0) {
return;
}
// 工作区域
let workArea = displays[0].workArea;
// 缩放比例
let scaleFactor = displays[0].scaleFactor;
// 窗口位置信息
let bounds = mainWindow.getBounds();
if (mainWindow.isVisible()) {
let flag = false;
if (bounds.x + bounds.width >= workArea.x + workArea.width) {
// 右侧
flag =
x <= bounds.x * scaleFactor - size ||
y <= bounds.y * scaleFactor - size ||
y >= (bounds.y + bounds.height) * scaleFactor + size;
} else if (bounds.x === workArea.x) {
// 左侧
flag =
x > (bounds.x + bounds.width) * scaleFactor + size ||
y <= bounds.y * scaleFactor - size ||
y >= (bounds.y + bounds.height) * scaleFactor + size;
} else if (bounds.y + bounds.height >= workArea.y + workArea.height) {
// 底部
flag =
y < bounds.y * scaleFactor - size ||
x <= bounds.x * scaleFactor - size ||
x >= (bounds.x + bounds.width) * scaleFactor + size;
} else if (bounds.y === workArea.y) {
// 顶部
flag =
y > (bounds.y + bounds.height) * scaleFactor + size ||
x <= bounds.x * scaleFactor - size ||
x >= (bounds.x + bounds.width) * scaleFactor + size;
}
if (flag && !global.classificationRightMenu && !global.itemRightMenu) {
if (
timer &&
global.setting.general.delayHideMs > 0 &&
!global.autoHideTimer
) {
// 延迟隐藏
global.autoHideTimer = setTimeout(function () {
hideMainWindow();
}, global.setting.general.delayHideMs);
} else if (!timer || global.setting.general.delayHideMs === 0) {
// 隐藏
hideMainWindow();
}
} else {
// 清空timer
clearTimeout(global.autoHideTimer);
global.autoHideTimer = null;
}
} else {
if (global.mainWindowDirection) {
let flag = false;
let scaleFactorX = bounds.x * scaleFactor;
let scaleFactorY = bounds.y * scaleFactor;
let windowWidthPosition = (bounds.x + bounds.width) * scaleFactor;
let windowHeightPosition = (bounds.y + bounds.height) * scaleFactor;
if (
global.mainWindowDirection === "right" &&
x >= windowWidthPosition - 1 &&
y >= scaleFactorY &&
y <= windowHeightPosition
) {
// 右侧
flag = true;
} else if (
global.mainWindowDirection === "left" &&
x <= workArea.x &&
y >= scaleFactorY &&
y <= windowHeightPosition
) {
// 左侧
flag = true;
} else if (
global.mainWindowDirection === "bottom" &&
y >= windowHeightPosition - 1 &&
x >= scaleFactorX &&
x <= windowWidthPosition
) {
// 底部
flag = true;
} else if (
global.mainWindowDirection === "top" &&
y <= workArea.y &&
x >= scaleFactorX &&
x <= windowWidthPosition
) {
// 顶部
flag = true;
}
if (flag) {
if (
timer &&
global.setting.general.delayDisplayMs > 0 &&
!global.autoHideTimer
) {
// 延迟显示
global.autoHideTimer = setTimeout(function () {
showMainWindowBefore(false);
}, global.setting.general.delayDisplayMs);
} else if (!timer || global.setting.general.delayDisplayMs === 0) {
// 显示
showMainWindowBefore(false);
}
} else {
// 清空timer
clearTimeout(global.autoHideTimer);
global.autoHideTimer = null;
}
}
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
global.mainWindow.setBounds({ x: 1, y: 1 });
}
}
/**
* /
*/
function doubleClickTaskbar(button: number) {
// 必须开启设置
if (!global.setting.general.showHideDoubleClickTaskbar) {
return;
}
// 不是左键的话
if (button !== 1) {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
return;
}
// 获取屏幕
let displays = getWindowInScreen();
if (displays.length > 1 || displays.length === 0) {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
return;
}
// 获取鼠标位置
let point = screen.getCursorScreenPoint();
// 判断鼠标是否在当前屏幕内
if (
point.x >= displays[0].bounds.x &&
point.x <= displays[0].bounds.x + displays[0].bounds.width &&
point.y >= displays[0].bounds.y &&
point.y <= displays[0].bounds.y + displays[0].bounds.height
) {
// 判断是否双击在任务栏上
let flag = false;
// 判断任务栏在哪一侧
if (displays[0].bounds.height > displays[0].workArea.height) {
if (displays[0].bounds.y === displays[0].workArea.y) {
// 底部
let top = displays[0].workArea.y + displays[0].workArea.height;
let bottom = displays[0].bounds.y + displays[0].bounds.height;
if (point.y >= top && point.y <= bottom) {
flag = true;
}
} else {
// 顶部
if (
point.y >= displays[0].bounds.y &&
point.y <= displays[0].workArea.y
) {
flag = true;
}
}
} else if (displays[0].bounds.width > displays[0].workArea.width) {
if (displays[0].bounds.x === displays[0].workArea.x) {
// 右侧
let left = displays[0].workArea.x + displays[0].workArea.width;
let right = displays[0].bounds.x + displays[0].bounds.width;
if (point.x >= left && point.x <= right) {
flag = true;
}
} else {
// 左侧
if (
point.x >= displays[0].bounds.x &&
point.x <= displays[0].workArea.x
) {
flag = true;
}
}
}
if (flag) {
// 监听双击
if (!global.doubleClickTaskbarCounter) {
global.doubleClickTaskbarCounter = 0;
}
// +1
global.doubleClickTaskbarCounter++;
// 等于2就是双击
if (
global.doubleClickTaskbarCounter &&
global.doubleClickTaskbarCounter === 2
) {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
// 判断点击的窗口ClassName
let className = global.addon.getCursorPosWindowClassName();
if (className.indexOf("MSTask") >= 0 || className === "Shell_TrayWnd") {
if (mainWindow.isVisible()) {
hideMainWindow();
} else {
showMainWindowBefore(false);
}
}
} else {
// 间隔为500毫秒如果超过500毫秒就代表不是双击
global.doubleClickTaskbarTimer = setTimeout(function () {
global.doubleClickTaskbarCounter = 0;
}, 500);
}
} else {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
}
} else {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
}
}
export {
createMainWindow,
showMainWindowBefore,
showMainWindow,
hideMainWindow,
createTray,
edgeAdsorb,
getWindowInScreen,
showHideMouseWheelClick,
alwaysCenter,
autoHide,
};

View File

@ -0,0 +1,33 @@
import { ipcMain } from "electron";
import { createTray, hideMainWindow, showMainWindow } from "./index";
import { initAssociateFolder } from "../classification";
import { checkInvalid } from "../item";
export default function () {
// 显示窗口
ipcMain.on("showMainWindow", (event, args) => {
showMainWindow(args.blurHide);
});
// 隐藏窗口
ipcMain.on("hideMainWindow", () => {
hideMainWindow();
});
// 托盘
ipcMain.on("setTray", (event, args) => {
createTray(args);
});
// 初始化数据
ipcMain.on("mainWindowInitData", () => {
// 初始化关联文件夹
initAssociateFolder();
// 检测无效项目
if (global.setting.item.checkInvalidItem) {
// 五分钟检测一次
global.checkInvalidItemInterval = setInterval(() => {
checkInvalid();
}, 300000);
// 初始化执行一次
checkInvalid();
}
});
}

View File

@ -0,0 +1,131 @@
import { BrowserWindow, shell } from "electron";
import { join } from "node:path";
import { getMainBackgorunColor, sendToWebContent } from "../commons";
import cacheData from "../commons/cacheData";
// 窗口
let quickSearchWindow: BrowserWindow | null = null;
/**
*
*/
function createQuickSearchWindow() {
// 创建窗口
global.quickSearchWindowInit = false;
quickSearchWindow = global.quickSearchWindow = new BrowserWindow({
title: "Dawn Launcher",
width: 600,
height: 44,
type: "toolbar",
frame: false,
show: false,
maximizable: false,
minimizable: false,
fullscreenable: false,
resizable: false,
alwaysOnTop: true,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
backgroundThrottling: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
quickSearchWindow.loadURL(
process.env.VITE_DEV_SERVER_URL + "Search/QuickSearch"
);
} else {
quickSearchWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/Search/QuickSearch",
});
}
quickSearchWindow.webContents.on("did-finish-load", function () {
// 恢复上一次的位置
let bounds = cacheData.cacheStore.get("quickSearchWindowBounds");
if (bounds) {
quickSearchWindow.setBounds(bounds);
}
// 设置可以显示
global.searchWindowShow = true;
});
quickSearchWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 失去焦点
quickSearchWindow.on("blur", () => {
// 失去焦点后隐藏
if (global.setting.quickSearch.hideLoseFocus) {
hideQuickSearchWindow();
}
});
// 显示窗口
quickSearchWindow.on("show", () => {
// 背景色
quickSearchWindow.setBackgroundColor(
global.setting.appearance.theme.mainBackgroundColor
);
// 显示窗口时将输入法切换为英文模式
if (global.setting.general.switchEnglish) {
global.addon.switchEnglish(
quickSearchWindow.getNativeWindowHandle().readInt32LE(0)
);
}
});
// 窗口移动完毕
quickSearchWindow.on("moved", () => {
// 记录位置
cacheData.cacheStore.set(
"quickSearchWindowBounds",
quickSearchWindow.getBounds()
);
});
// 隐藏窗口
quickSearchWindow.on("hide", () => {
// 设置默认高度
quickSearchWindow.setBounds({ height: 44 });
// 发送消息清空数据
quickSearchWindow.webContents.send("onQuickSearchClearData");
});
// 禁用标题栏右键
quickSearchWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
quickSearchWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
quickSearchWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
*/
function showQuickSearchWindowBefore() {
// 向主窗口发送通知
sendToWebContent("quickSearchWindow", "onShowQuickSearchWindowBefore", null);
}
/**
*
*/
function showQuickSearchWindow() {
quickSearchWindow.show();
}
/**
*
*/
function hideQuickSearchWindow() {
quickSearchWindow.hide();
}
export {
createQuickSearchWindow,
showQuickSearchWindow,
hideQuickSearchWindow,
showQuickSearchWindowBefore,
};

View File

@ -0,0 +1,21 @@
import { ipcMain } from "electron";
import { hideQuickSearchWindow, showQuickSearchWindow } from ".";
export default function () {
// 快速搜索初始化完成
ipcMain.on("quickSearchInitFinished", () => {
global.quickSearchWindowInit = true;
});
// 显示快速搜索窗口
ipcMain.on("showQuickSearchWindow", () => {
showQuickSearchWindow();
});
// 隐藏快速搜索窗口
ipcMain.on("hideQuickSearchWindow", () => {
hideQuickSearchWindow();
});
// 设置快速搜索窗口高度
ipcMain.on("setQuickSearchWindowHeight", (event, args) => {
global.quickSearchWindow.setBounds({ height: args });
});
}

View File

@ -0,0 +1,82 @@
import { Setting } from "../../../types/setting";
import { getSetting } from "../../../commons/utils/setting";
import { getDataSqlite3 } from "../../commons/betterSqlite3";
// 获取数据库
let db = getDataSqlite3();
// 设置表名
let settingTableName = "setting";
/**
*
*/
function init() {
// sql
let sql = `CREATE TABLE IF NOT EXISTS ${settingTableName} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
setting TEXT NOT NULL)`;
// 运行
db.exec(sql);
// 如果无数据的话,初始化
let setting = select();
if (setting) {
global.setting = setting;
} else {
setting = getSetting(null);
if (add(setting)) {
global.setting = setting;
}
}
}
/**
*
*/
function select() {
// sql
let sql = `SELECT setting FROM ${settingTableName} WHERE id = 1`;
// 运行
let row: any = db.prepare(sql).get();
// 返回
if (row && row.setting) {
return getSetting(JSON.parse(row.setting));
} else {
return null;
}
}
/**
*
*/
function add(setting: Setting) {
// SQL
let sql = `INSERT INTO ${settingTableName}
(id, setting)
VALUES (?, ?)`;
// 运行
let id = db.prepare(sql).run(1, JSON.stringify(setting)).lastInsertRowid;
if (id) {
global.setting = setting;
return true;
}
return false;
}
/**
*
*/
function update(setting: Setting) {
// SQL
let sql = `UPDATE ${settingTableName}
SET setting = ?
WHERE id = ?`;
// 运行
let res = db.prepare(sql).run(JSON.stringify(setting), 1).changes > 0;
if (res) {
global.setting = setting;
}
return res;
}
export { init, select, add, update };

View File

@ -0,0 +1,146 @@
import { BrowserWindow, globalShortcut, shell } from "electron";
import { join } from "node:path";
import { Setting } from "../../../types/setting";
import { hideMainWindow, showMainWindowBefore } from "../main/index";
import { list as selectClassificationList } from "../classification/data";
import { list as selectItemList } from "../item/data";
import { run } from "../item";
import { closeWindow, getMainBackgorunColor } from "../commons/index";
import {
createQuickSearchWindow,
hideQuickSearchWindow,
showQuickSearchWindowBefore,
} from "../search";
// 窗口
let settingWindow: BrowserWindow | null = null;
/**
*
*/
function createSettingWindow() {
// 如果窗口存在先关闭窗口
closeWindow(settingWindow);
// 创建窗口
settingWindow = global.settingWindow = new BrowserWindow({
title: "Dawn Launcher",
frame: false,
parent: global.mainWindow,
height: 500,
width: 600,
type: "toolbar",
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
skipTaskbar: true,
show: false,
backgroundColor: getMainBackgorunColor(),
webPreferences: {
spellcheck: false,
preload: join(__dirname, "../preload/index.js"),
devTools: process.env.NODE_ENV === "development",
},
});
if (process.env.VITE_DEV_SERVER_URL) {
settingWindow.loadURL(process.env.VITE_DEV_SERVER_URL + "Setting/Index");
} else {
settingWindow.loadFile(join(process.env.DIST, "index.html"), {
hash: "/Setting/Index",
});
}
settingWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith("https:")) shell.openExternal(url);
return { action: "deny" };
});
// 禁用标题栏右键
settingWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
settingWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
settingWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
*/
function setShortcutKey(setting: Setting = global.setting) {
// 取消所有快捷键
globalShortcut.unregisterAll();
// 设置主窗口显示/隐藏快捷键
if (
setting.general.showHideShortcutKey &&
setting.general.showHideShortcutKey.trim() !== ""
) {
globalShortcut.register(setting.general.showHideShortcutKey, () => {
if (global.mainWindow.isVisible()) {
hideMainWindow();
} else {
showMainWindowBefore(true);
}
});
}
// 分类快捷键
let classificationList = selectClassificationList();
for (const classification of classificationList) {
if (classification.globalShortcutKey && classification.shortcutKey) {
globalShortcut.register(classification.shortcutKey, () => {
// 分类
showMainWindowBefore(true, classification.id);
});
}
}
// 项目快捷键
let itemList = selectItemList();
for (const item of itemList) {
if (item.globalShortcutKey && item.shortcutKey) {
globalShortcut.register(item.shortcutKey, () => {
// 项目
run("main", "open", item);
});
}
}
// 快速搜索
if (
setting.quickSearch.showHideShortcutKey &&
setting.quickSearch.showHideShortcutKey.trim() !== ""
) {
globalShortcut.register(setting.quickSearch.showHideShortcutKey, () => {
// 如果窗口不存在或者被销毁的话,就创建窗口
if (!global.quickSearchWindow || global.quickSearchWindow.isDestroyed()) {
createQuickSearchWindow();
}
// 如果初始化完毕并且窗口状态是正常的话,可以进行显示/隐藏
if (
global.quickSearchWindowInit &&
global.quickSearchWindow &&
!global.quickSearchWindow.isDestroyed()
) {
if (global.quickSearchWindow.isVisible()) {
hideQuickSearchWindow();
} else {
showQuickSearchWindowBefore();
}
}
});
}
}
/**
*
* @param fixedPosition
* @param alwaysCenter
*/
function setFixedPosition(fixedPosition: boolean, alwaysCenter: boolean) {
global.mainWindow.setMovable(!fixedPosition);
// 固定位置和永远居中不能同时存在
if (alwaysCenter && fixedPosition) {
global.mainWindow.setMovable(false);
}
}
export { createSettingWindow, setShortcutKey, setFixedPosition };

View File

@ -0,0 +1,207 @@
import { app, dialog, ipcMain } from "electron";
import {
closeWindow,
getUserDataPath,
relaunch,
sendToWebContent,
} from "../commons/index";
import { createSettingWindow, setFixedPosition, setShortcutKey } from ".";
import { add, select, update } from "./data";
import { basename } from "node:path";
import { edgeAdsorb } from "../main";
import { sendAllWindows } from "../commons";
import { parse } from "node:path";
import { statSync, mkdirSync, copyFileSync, readFileSync } from "node:fs";
import mime from "mime";
import { checkInvalid } from "../item";
import { updateItemOpenNumberSortToDefualt } from "../classification";
export default function () {
// 创建设置窗口
ipcMain.on("createSettingWindow", () => {
createSettingWindow();
});
// 显示设置窗口
ipcMain.on("showSettingWindow", () => {
if (global.settingWindow) {
global.settingWindow.show();
}
});
// 关闭设置窗口
ipcMain.on("closeSettingWindow", () => {
closeWindow(global.settingWindow);
});
// 查询设置
ipcMain.on("selectSetting", (event, args) => {
event.returnValue = select();
});
// 添加设置
ipcMain.on("addSetting", (event, args) => {
event.returnValue = add(args);
});
// 更新设置
ipcMain.on("updateSetting", (event, args) => {
// 记录旧透明度
let oldTransparency = global.setting.appearance.transparency;
// 记录旧语言
let oldLanguage = global.setting.general.language;
// 更新
event.returnValue = update(args);
// 判断是否需要重启主界面
if (
(oldTransparency === 1 && global.setting.appearance.transparency < 1) ||
(oldTransparency < 1 && global.setting.appearance.transparency === 1)
) {
// 重新启动程序
relaunch();
return;
} else if (global.setting.general.language !== oldLanguage) {
// 重新启动程序
relaunch();
return;
}
// 通知所有窗口
sendAllWindows("onUpdateSetting", args);
});
// 开机启动
ipcMain.on("startup", (event, args) => {
if (process.env.NODE_ENV !== "development") {
const exeName = basename(process.execPath);
app.setLoginItemSettings({
openAtLogin: args,
openAsHidden: false,
path: process.execPath,
args: ["--processStart", `"${exeName}"`],
});
}
});
// 设置快捷键
ipcMain.on("setShortcutKey", (event, args) => {
setShortcutKey(args);
});
// 永远置顶
ipcMain.on("setAlwaysTop", (event, args) => {
if (args) {
global.mainWindow.setAlwaysOnTop(true, "screen-saver");
} else {
global.mainWindow.setAlwaysOnTop(false);
}
});
// 锁定尺寸
ipcMain.on("setLockSize", (event, args) => {
global.mainWindow.setResizable(!args);
});
// 固定位置
ipcMain.on("setFixedPosition", (event, args) => {
setFixedPosition(args.fixedPosition, args.alwaysCenter);
});
// 永远居中
ipcMain.on("setAlwaysCenter", (event, args) => {
if (args.alwaysCenter) {
global.mainWindow.center();
global.mainWindow.setMovable(false);
} else {
setFixedPosition(args.fixedPosition, args.alwaysCenter);
}
});
// 边缘吸附
ipcMain.on("setEdgeAdsorb", (event, args) => {
if (args) {
global.setting.general.edgeAdsorb = true;
edgeAdsorb(null);
}
});
// 上传背景图
ipcMain.on("uploadBackgrounImage", (event, args) => {
// 打开文件对话框
let filePathList = dialog.showOpenDialogSync(global.settingWindow, {
filters: [
{
name: "Images",
extensions: ["jpg", "jpeg", "png", "gif", "ico", "svg", "webp"],
},
],
});
if (filePathList && filePathList.length > 0) {
// 获取文件路径
let filePath = filePathList[0];
// 解析路径
let parsedPath = parse(filePath);
// 拷贝的路径
let destPath = getUserDataPath() + "\\images";
// 不存在目录,创建目录
try {
statSync(destPath);
} catch (e) {
mkdirSync(destPath);
}
// 图片名
let name = "backgroundImage" + parsedPath.ext;
// 全路径
let copyFullPath = destPath + "\\" + name;
try {
// 拷贝
copyFileSync(filePath, copyFullPath);
// 图片转base64
let buffer = readFileSync(copyFullPath);
let image =
"data:" +
mime.getType(copyFullPath) +
";base64," +
buffer.toString("base64");
// 返回base64
event.returnValue = {
name,
image,
};
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
event.returnValue = null;
}
} else {
event.returnValue = null;
}
});
// 获取背景图
ipcMain.on("getBackgroundImage", (event, args) => {
// 图片名
let name: string = args.name;
// 窗口
let windowName: string = args.windowName;
try {
// 读取图片转为BASE64
let data = readFileSync(getUserDataPath() + "\\images\\" + name);
let buffer = Buffer.from(data);
let image =
"data:" + mime.getType(name) + ";base64," + buffer.toString("base64");
// 通知窗口
sendToWebContent(windowName, "onSetBacngroundImage", image);
} catch (e) {}
});
// 检测无效项目
ipcMain.on("setCheckInvalidItem", (event, args) => {
if (args) {
if (!global.checkInvalidItemInterval) {
// 五分钟检测一次
global.checkInvalidItemInterval = setInterval(() => {
checkInvalid();
}, 300000);
// 执行一次
checkInvalid();
}
} else {
// 清空定时
clearInterval(global.checkInvalidItemInterval);
global.checkInvalidItemInterval = null;
}
});
// 项目打开次数
ipcMain.on("setItemOpenNumber", (event, args) => {
if (!args) {
// 将排序为打开次数的分类修改为默认排序
updateItemOpenNumberSortToDefualt();
}
});
}

793
electron/main/worker.ts Normal file
View File

@ -0,0 +1,793 @@
import mime from "mime";
import {
deleteExtname,
getFileName,
newItem,
} from "../../commons/utils/common";
import { CommonItem, Item } from "../../types/item";
import { parse, join } from "node:path";
import { readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { execSync } from "node:child_process";
import xml2js from "xml2js";
import { newCommonItem, newCommonItemData } from "../../commons/utils/common";
import { ShortcutInfo } from "../../types/common";
import { getAbsolutePath, getFileIcon } from "../commons/utils";
// AppxInfo
export interface AppxInfo {
packageFamilyName: string;
installLocation: string;
appId: string | null;
icon: string | null;
name: string | null;
}
// addon
global.addon = require("../../native/addon.node");
// 接收消息
process.parentPort.once("message", async (event) => {
// 参数
let data = event.data;
// 通道
let port = event.ports[0];
try {
// 转为实体
let params = JSON.parse(data);
// 获取数据参数
let dataParamStr = readFileSync(params.data.filePath, {
encoding: "utf-8",
});
// 转为JSON
let dataParam = JSON.parse(dataParamStr);
// 返回信息
let res = null;
if (params.name === "getStartMenuItemList") {
res = await getStartMenuItemList(dataParam);
} else if (params.name === "getAppxItemList") {
res = await getAppxItemList();
} else if (params.name === "getDropItemInfo") {
res = await getDropItemInfo(
dataParam.classificationId,
dataParam.pathList
);
} else if (params.name === "refreshItemIcon") {
res = await refreshItemIcon(dataParam);
} else if (params.name === "getDirectoryItemList") {
res = await getDirectoryItemList(
dataParam.classificationId,
dataParam.dir,
dataParam.hiddenItems,
dataParam.oldList
);
} else if (params.name === "checkInvalidItem") {
res = checkInvalidItem(dataParam);
}
// 写入结果
writeFileSync(params.data.filePath, JSON.stringify(res));
port.postMessage(params.data.filePath);
} catch (e) {
process.exit();
}
});
/**
*
* @param dir
*/
function getFiles(dir: string) {
let resultList: Array<string> = [];
try {
// 读取开始菜单下所有内容
let pathList = readdirSync(dir);
// 循环判断文件类型
for (let path of pathList) {
// 完整路径
let fullPath = dir + "\\" + path;
// 判断文件类型
let stats;
try {
// 文件类型
stats = statSync(fullPath);
// 如果是文件夹继续向下读取,如果是文件则添加到返回列表
if (stats.isDirectory()) {
// 文件夹
resultList.push(...getFiles(fullPath));
} else {
// 文件
resultList.push(fullPath);
}
} catch (e) {}
}
} catch (e) {}
return resultList;
}
/**
*
* @param cacheList
*/
async function getStartMenuItemList(cacheList: Array<CommonItem>) {
// 返回列表
let resultList: Array<CommonItem> = [];
// appData
let appDataPathList = getFiles(
process.env["AppData"] + "\\Microsoft\\Windows\\Start Menu\\Programs"
);
// programData
let programDataPathList = getFiles(
process.env["ProgramData"] + "\\Microsoft\\Windows\\Start Menu\\Programs"
);
// 文件列表
let filePathList: Array<string> = [];
filePathList.push(...appDataPathList);
filePathList.push(...programDataPathList);
// 循环组装数据
for (let filePath of filePathList) {
// 获取后缀,必须是快捷方式
if (mime.getType(filePath) === "application/x-ms-shortcut") {
// 获取名称去掉后缀
let name = deleteExtname(getFileName(filePath));
// 参数
let params = null;
// 获取真实文件路径和参数
let shortcutInfo: ShortcutInfo | null =
global.addon.getShortcutFileInfo(filePath);
if (shortcutInfo) {
// 路径
if (shortcutInfo.target) {
filePath = shortcutInfo.target;
}
// 参数
if (shortcutInfo.arguments) {
params = shortcutInfo.arguments;
}
}
// 查重
let flag = false;
for (let item of resultList) {
if (item.data.target.toLowerCase() === filePath.toLowerCase()) {
flag = true;
break;
}
}
if (!flag) {
let exist = false;
// 是否存在如果存在的话不需要重新获取图标
if (cacheList && cacheList.length > 0) {
for (let cacheItem of cacheList) {
if (filePath === cacheItem.data.target) {
resultList.push(newCommonItem(cacheItem));
exist = true;
break;
}
}
}
if (!exist) {
// item
let item = newCommonItem({
name,
data: newCommonItemData({
target: filePath,
icon: getFileIcon(filePath),
params,
}),
});
// push
resultList.push(item);
}
}
}
}
return resultList;
}
/**
* APPX项目
*/
async function getAppxItemList() {
// 返回列表
let resultList: Array<CommonItem> = [];
try {
// ID
let id = 1;
// 获取APPX信息
let stdout = execSync(
'powershell -Command "Get-AppxPackage | Select-Object PackageFamilyName, InstallLocation | Format-list"'
);
let strAppxInfo = stdout.toString("utf-8");
// 按换行符分割
let lines = strAppxInfo
.trim()
.split("\r\n")
.filter((str) => str.trim() !== "");
// 临时列表
let tempList: Array<AppxInfo> = [];
// APPX包名
let packageFamilyName: string | null = null;
// APPX路径
let installLocation: string | null = null;
// 循环的前一个信息
let prev = null;
// 解析每一行
for (let i = 0; i < lines.length; i++) {
let line = lines[i].trim();
let arr = line.split(" : ");
if (arr.length > 1) {
if (arr[0].trim() === "PackageFamilyName") {
if (packageFamilyName && installLocation) {
tempList.push({
packageFamilyName: packageFamilyName,
installLocation: installLocation,
appId: null,
icon: null,
name: null,
});
packageFamilyName = arr[1].trim();
installLocation = null;
prev = "PackageFamilyName";
} else {
packageFamilyName = arr[1].trim();
prev = "PackageFamilyName";
}
} else if (arr[0].trim() === "InstallLocation") {
installLocation = arr[1].trim();
prev = "InstallLocation";
}
} else {
if (prev === "PackageFamilyName") {
packageFamilyName += line;
} else if (prev === "InstallLocation") {
installLocation += line;
}
}
}
if (packageFamilyName && installLocation) {
tempList.push({
packageFamilyName: packageFamilyName,
installLocation: installLocation,
appId: null,
icon: null,
name: null,
});
}
// 读取XML获取图标路径和名称
for (let temp of tempList) {
let appxInfo = await getAppxInfo(temp.installLocation);
temp.appId = appxInfo.appId;
temp.icon = appxInfo.icon;
temp.name = appxInfo.name;
}
// 过滤
let filterList = tempList.filter((e) => e.icon && e.appId && e.name);
// 图标转BASE64
for (let appxInfo of filterList) {
try {
let buffer = readFileSync(appxInfo.icon);
let icon =
"data:" +
mime.getType(appxInfo.icon) +
";base64," +
buffer.toString("base64");
appxInfo.icon = icon;
} catch (ex) {
appxInfo.icon = null;
}
}
// 筛选出有图标的数据
filterList = filterList.filter((e) => e.icon);
// 返回列表
for (const appxInfo of filterList) {
resultList.push(
newCommonItem({
id: id++,
name: appxInfo.name,
data: newCommonItemData({
icon: appxInfo.icon,
target:
"Shell:AppsFolder\\" +
appxInfo.packageFamilyName +
"!" +
appxInfo.appId,
}),
})
);
}
// 排序
resultList.sort((a, b) => a.name.localeCompare(b.name));
} catch (e) {}
return resultList;
}
/**
* Appx信息
*/
async function getAppxInfo(installLocation: string) {
// appx信息
let appxInfo: AppxInfo = {
packageFamilyName: null,
installLocation: null,
appId: null,
icon: null,
name: null,
};
// buffer, 解析结果
let buffer: Buffer, result: any;
try {
// 解析
buffer = readFileSync(installLocation + "\\AppxManifest.xml");
result = await xml2jsSync(buffer);
// 备用名称
let executable = null;
// targetsize图标
let targetSizeIcon: string | null = null;
let targetSizeIconMax: number | null = null;
// scale图标
let scaleIcon = null;
let scaleIconMax = null;
// 图标 APPID
if (result.Package.Applications && result.Package.Applications[0]) {
if (result.Package.Applications[0].Application[0]) {
// APPID
appxInfo.appId = result.Package.Applications[0].Application[0].$.Id;
// Executable
executable = result.Package.Applications[0].Application[0].$.Executable;
// 获取图标
if (
result.Package.Applications[0].Application[0]["uap:VisualElements"] !=
null
) {
// logo地址
let logo =
result.Package.Applications[0].Application[0][
"uap:VisualElements"
][0].$.Square44x44Logo;
// 解析路径
let parsedPath = parse(logo);
// 获取文件夹下所有文件
let fileNameList = readdirSync(
installLocation + "\\" + parsedPath.dir
);
// 筛选出和包含logo名称的文件名
let filterList = fileNameList.filter(
(f) => f.indexOf(parsedPath.name) >= 0
);
if (filterList.length > 1) {
// 获取targetsize图片
let targetSizeList = filterList.filter(
(f) => f.indexOf(parsedPath.name + ".targetsize") >= 0
);
if (targetSizeList.length > 0) {
// 获取最大图标尺寸
let max = getMaxIconSize(
targetSizeList,
parsedPath.name,
"targetsize"
);
if (max) {
// 记录max
targetSizeIconMax = max;
// 先获取最终图标
let defaultList = targetSizeList.filter(
(f) =>
f ===
parsedPath.name +
".targetsize-" +
max +
"_altform-unplated_devicefamily-colorfulunplated.png"
);
targetSizeIcon =
defaultList.length > 0
? installLocation +
"\\" +
parsedPath.dir +
"\\" +
parsedPath.name +
".targetsize-" +
max +
"_altform-unplated_devicefamily-colorfulunplated.png"
: null;
if (!targetSizeIcon) {
// 获取 名称.targetsize-{max}_altform-unplated.png
let defaultUnplatedList = targetSizeList.filter(
(f) =>
f ===
parsedPath.name +
".targetsize-" +
max +
"_altform-unplated.png"
);
if (defaultUnplatedList.length > 0) {
targetSizeIcon =
installLocation +
"\\" +
parsedPath.dir +
"\\" +
parsedPath.name +
".targetsize-" +
max +
"_altform-unplated.png";
} else {
// 获取 名称.targetsize-{max}_altform.png
let defaultAltFormList = targetSizeList.filter(
(f) =>
f ===
parsedPath.name + ".targetsize-" + max + "_altform.png"
);
if (defaultAltFormList.length > 0) {
targetSizeIcon =
installLocation +
"\\" +
parsedPath.dir +
"\\" +
parsedPath.name +
".targetsize-" +
max +
"_altform.png";
} else {
// 获取 名称.targetsize-{max}.png
let defaultTargetSizeList = targetSizeList.filter(
(f) =>
f === parsedPath.name + ".targetsize-" + max + ".png"
);
if (defaultTargetSizeList.length > 0) {
targetSizeIcon =
installLocation +
"\\" +
parsedPath.dir +
"\\" +
defaultTargetSizeList[0];
}
}
}
}
}
}
// 获取scale图片
let scaleList = filterList.filter(
(f) => f.indexOf(parsedPath.name + ".scale") >= 0
);
if (scaleList.length > 0) {
// 获取最大图标尺寸
let max = getMaxIconSize(scaleList, parsedPath.name, "scale");
if (max) {
// 记录max
scaleIconMax = max;
// 获取 名称.scale-{max}.png
let defaultList = scaleList.filter(
(f) => f === parsedPath.name + ".scale-" + max + ".png"
);
if (defaultList.length > 0) {
scaleIcon =
installLocation +
"\\" +
parsedPath.dir +
"\\" +
defaultList[0];
}
}
} else {
scaleList = filterList.filter(
(f) => f.indexOf(parsedPath.name + ".Theme-Dark_Scale") >= 0
);
if (scaleList.length > 0) {
let max = getMaxIconSize(
scaleList,
parsedPath.name,
"Theme-Dark_Scale"
);
if (max) {
// 记录max
scaleIconMax = max;
// 获取 名称.Theme-Dark_Scale{max}.png
let defaultList = scaleList.filter(
(f) =>
f ===
parsedPath.name + ".Theme-Dark_Scale-" + max + ".png"
);
if (defaultList.length > 0) {
scaleIcon =
installLocation +
"\\" +
parsedPath.dir +
"\\" +
defaultList[0];
}
}
}
}
} else {
if (filterList.length === 1) {
// 只有一张图片
appxInfo.icon =
installLocation + "\\" + parsedPath.dir + "\\" + filterList[0];
}
}
}
}
}
if (!appxInfo.icon) {
// 判断图标大小
if (targetSizeIcon && !scaleIcon) {
appxInfo.icon = targetSizeIcon;
} else if (!targetSizeIcon && scaleIcon) {
appxInfo.icon = scaleIcon;
} else if (targetSizeIcon && scaleIcon) {
if (targetSizeIconMax === 256 || targetSizeIconMax > scaleIconMax) {
appxInfo.icon = targetSizeIcon;
} else if (targetSizeIconMax < scaleIconMax) {
appxInfo.icon = scaleIcon;
} else {
appxInfo.icon = targetSizeIcon;
}
} else if (!targetSizeIcon && !scaleIcon) {
let propertiesIcon = getPropertiesIcon(installLocation, result);
if (propertiesIcon) {
appxInfo.icon = propertiesIcon;
}
}
}
// 名称
if (result.Package.Properties) {
if (result.Package.Properties[0].DisplayName) {
appxInfo.name = result.Package.Properties[0].DisplayName[0];
}
}
if (
!appxInfo.name ||
(appxInfo.name && appxInfo.name.indexOf("ms-resource:") >= 0)
) {
if (executable && executable.indexOf("ms-resource:") < 0) {
appxInfo.name = parse(executable).name;
} else {
appxInfo.name = null;
}
}
if (!appxInfo.name) {
if (result.Package.Identity && result.Package.Identity[0]) {
let name = result.Package.Identity[0].$.Name;
if (name && name.indexOf("ms-resource:") < 0) {
appxInfo.name = name;
}
}
}
} catch (ex) {
if (result) {
let propertiesIcon = getPropertiesIcon(installLocation, result);
if (propertiesIcon) {
appxInfo.icon = propertiesIcon;
}
}
}
return appxInfo;
}
/**
* XML同步
* @param xml
*/
async function xml2jsSync(xml: Buffer) {
let parser = new xml2js.Parser();
return new Promise((resolve, reject) => {
parser.parseString(xml, function (err, json) {
if (err) reject(err);
else resolve(json);
});
});
}
/**
*
* @param list
* @param name
* @param type
* @returns
*/
function getMaxIconSize(list: Array<string>, name: string, type: string) {
// 获取最大图标尺寸
let max: number | null = null;
for (let targetSize of list) {
let size = Number(
targetSize
.replace(name + "." + type + "-", "")
.split("_")[0]
.replace(".png", "")
);
if (!max) {
max = size;
} else {
if (size > max) {
max = size;
}
}
}
return max;
}
/**
* AppxPropertiesLogo
* @param installLocation
* @param result
*/
function getPropertiesIcon(installLocation: string, result: any) {
if (result.Package.Properties) {
if (result.Package.Properties[0].Logo) {
let logo = result.Package.Properties[0].Logo[0];
return installLocation + "\\" + logo;
}
}
return null;
}
/**
*
* @param classificationId
* @param pathList
*/
async function getDropItemInfo(
classificationId: number,
pathList: Array<string>
) {
// 项目列表
let itemList: Array<Item> = [];
// 解析文件信息并添加项目
for (const path of pathList) {
try {
// item
let item = newItem({ classificationId });
// 目标
item.data.target = path;
// 名称
item.name = getFileName(item.data.target);
// 判断是否是快捷方式,如果是的话,需要获取真实路径
if (mime.getType(path) === "application/x-ms-shortcut") {
// 快捷方式
// 获取真实文件路径和参数
let shortcutInfo: ShortcutInfo | null =
global.addon.getShortcutFileInfo(path);
if (shortcutInfo) {
// 路径
if (shortcutInfo.target) {
item.data.target = shortcutInfo.target;
}
// 参数
if (shortcutInfo.arguments) {
item.data.params = shortcutInfo.arguments;
}
}
}
// 文件类型
let stats = statSync(item.data.target);
// 路径
item.type = stats.isFile() ? 0 : 1;
// 获取图标
item.data.icon = getFileIcon(item.data.target);
// 去掉后缀
if (item.type === 0) {
item.name = deleteExtname(item.name);
}
// push
itemList.push(item);
} catch (e) {}
}
return itemList;
}
/**
*
* @param itemList
*/
async function refreshItemIcon(itemList: Array<Item>) {
// 返回数据
let resultList: Array<{
id: number;
icon: string;
}> = [];
// 刷新图标
for (const item of itemList) {
if (item.type === 0 || item.type === 1) {
let icon: string | null = getFileIcon(item.data.target);
if (icon) {
resultList.push({
id: item.id,
icon,
});
}
}
}
return resultList;
}
/**
*
* @param classificationId
* @param dir
* @param hiddenItems
* @param oldList
* @returns
*/
async function getDirectoryItemList(
classificationId: number,
dir: string,
hiddenItems: string | null,
oldList: Array<Item>
) {
// 返回列表
let resultList: Array<Item> = [];
// 转map
let oldMap = new Map(
oldList.map((item) => [item.data.target.toLowerCase(), item])
);
try {
// 文件类型
let stats = statSync(dir);
// 必须是文件夹
if (stats.isDirectory()) {
// 转为数组
let hiddenItemList = [];
if (hiddenItems && hiddenItems.trim() !== "") {
hiddenItemList = hiddenItems.split(",");
}
// 读取文件夹下面的所有文件
let nameList = readdirSync(dir);
for (const name of nameList) {
try {
// 判断是否隐藏
let hidden = false;
for (let hiddenItem of hiddenItemList) {
if (hiddenItem.trim().toLowerCase() === name.trim().toLowerCase()) {
hidden = true;
break;
}
}
if (hidden) {
continue;
}
// item
let item: Item | null = null;
// 组合路径
let path = join(dir, name);
// 获取类型 0:文件 1:文件夹
let type = statSync(path).isDirectory() ? 1 : 0;
// 是否有旧数据
if (oldMap.has(path.toLowerCase())) {
let oldItem = oldMap.get(path.toLowerCase());
if (oldItem.type === type) {
item = newItem(oldItem);
}
}
if (!item) {
item = newItem({ classificationId, type });
item.name = deleteExtname(getFileName(path));
item.data.target = path;
item.data.icon = getFileIcon(path);
}
// push
resultList.push(item);
} catch (e) {}
}
}
} catch (e) {}
return resultList;
}
/**
*
*/
function checkInvalidItem(itemList: Array<Item>) {
// 无效项目ID列表
let resultList: Array<number> = [];
// 循环校验每个项目
for (const item of itemList) {
// 只校验文件和文件夹
if (item.type === 0 || item.type === 1) {
// 获取绝对路径
let path = getAbsolutePath(item.data.target);
try {
statSync(path);
} catch (e) {
resultList.push(item.id);
}
}
}
return resultList;
}

850
electron/preload/index.ts Normal file
View File

@ -0,0 +1,850 @@
import { contextBridge, ipcRenderer } from "electron";
import { Classification } from "../../types/classification";
import { Item } from "../../types/item";
import { Setting } from "../../types/setting";
contextBridge.exposeInMainWorld("api", {
// emit
emit: (windowName: string, listener: string, paylod: any) => {
ipcRenderer.send("emit", { windowName, listener, paylod });
},
// 错误提示框
showErrorMessageBox: (windowName: string, message: string) => {
ipcRenderer.send("showErrorMessageBox", { windowName, message });
},
// 信息提示框
showInfoMessageBox: (windowName: string, message: string) => {
ipcRenderer.send("showInfoMessageBox", { windowName, message });
},
// 对话框
showConfirmBox: (windowName: string, message: string): boolean => {
return ipcRenderer.sendSync("showConfirmBox", { windowName, message });
},
// 选择文件
selectFile: (
windowName: string,
target: boolean,
defaultPath: string | null
): string | null => {
return ipcRenderer.sendSync("selectFile", {
windowName,
target,
defaultPath,
});
},
// 选择文件夹
selectDirectory: (
windowName: string,
defaultPath: string | null
): string | null => {
return ipcRenderer.sendSync("selectDirectory", {
windowName,
defaultPath,
});
},
// 获取图标
getFileIcon: (windowName: string, path: string) => {
ipcRenderer.send("getFileIcon", { windowName, path });
},
// 监听获取图标
onGetFileIcon: (callback): Function => {
ipcRenderer.on("onGetFileIcon", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onGetFileIcon");
};
},
// 下载图片
downloadImage: (windowName: string, url: string) => {
ipcRenderer.send("downloadImage", { windowName, url });
},
// 监听下载图片
onDownloadImage: (callback): Function => {
ipcRenderer.on("onDownloadImage", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onDownloadImage");
};
},
// 获取网址信息
getURLInfo: (windowName: string, url: string) => {
ipcRenderer.send("getURLInfo", { windowName, url });
},
// 监听获取网址信息
onGetURLInfo: (callback): Function => {
ipcRenderer.on("onGetURLInfo", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onGetURLInfo");
};
},
// 转换路径
convertPath: (path: string): string => {
return ipcRenderer.sendSync("convertPath", { path });
},
// 路径是否存在
pathExist: (path: string): boolean => {
return ipcRenderer.sendSync("pathExist", { path });
},
// 是否是文件
isFile: (path: string): boolean => {
return ipcRenderer.sendSync("isFile", { path });
},
// 打开URL
openURL: (url: string) => {
ipcRenderer.send("openURL", url);
},
// 获取版本
getVersion: (): string => {
return ipcRenderer.sendSync("getVersion");
},
// 退出
exit: () => {
ipcRenderer.send("exit");
},
});
contextBridge.exposeInMainWorld("main", {
// 显示窗口
showWindow: (blurHide: boolean) => {
ipcRenderer.send("showMainWindow", { blurHide });
},
// 隐藏窗口
hideWindow: () => {
ipcRenderer.send("hideMainWindow");
},
// 初始化数据
initData: () => {
ipcRenderer.send("mainWindowInitData");
},
// 显示窗口之前
onShowWindowBefore: (callback): Function => {
ipcRenderer.on("onShowMainWindowBefore", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onShowMainWindowBefore");
};
},
});
contextBridge.exposeInMainWorld("classification", {
// 获取分类列表
list: (): Array<Classification> => {
return ipcRenderer.sendSync("getClassificationList");
},
// 根据ID查询分类
selectById: (id: number): Classification | null => {
return ipcRenderer.sendSync("getClassificationById", { id });
},
// 新增分类
add: (
parentId: number | null,
name: string,
shortcutKey: string | null,
globalShortcutKey: boolean
): Classification | null => {
return ipcRenderer.sendSync("addClassification", {
parentId,
name,
shortcutKey,
globalShortcutKey,
});
},
// 更新分类
update: (classifictaion: Classification): boolean => {
return ipcRenderer.sendSync("updateClassification", classifictaion);
},
// 更新序号
updateOrder: (
fromId: number,
toId: number | null,
parentId: number | null
): boolean => {
return ipcRenderer.sendSync("updateClassificationOrder", {
fromId,
toId,
parentId,
});
},
// 更新图标
updateIcon: (id: number, icon: string | null): boolean => {
return ipcRenderer.sendSync("updateClassificationIcon", {
id,
icon,
});
},
// 显示新增/修改窗口
showAddEditWindow: () => {
ipcRenderer.send("showClassificationAddEditWindow");
},
// 关闭新增/修改窗口
closeAddEditWindow: () => {
ipcRenderer.send("closeClassificationAddEditWindow");
},
// 显示设置图标窗口
showSetIconWindow: () => {
ipcRenderer.send("showClassificationSetIconWindow");
},
// 关闭设置图标窗口
closeSetIconWindow: () => {
ipcRenderer.send("closeClassificationSetIconWindow");
},
// 右键菜单
showRightMenu: (
classification: Classification | null,
lockClassification: boolean
) => {
ipcRenderer.send("showClassificationRightMenu", {
classification,
lockClassification,
});
},
// 监听新增分类
onAdd: (callback): Function => {
ipcRenderer.on("onAddClassification", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onAddClassification");
};
},
// 监听更新分类
onUpdate: (callback): Function => {
ipcRenderer.on("onUpdateClassification", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateClassification");
};
},
// 监听删除分类
onDelete: (callback): Function => {
ipcRenderer.on("onDeleteClassification", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onDeleteClassification");
};
},
// 监听锁定/解锁分类
onLock: (callback): Function => {
ipcRenderer.on("onLockClassification", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onLockClassification");
};
},
// 更新分类图标
onUpdateIcon: (callback): Function => {
ipcRenderer.on("onUpdateClassificationIcon", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateClassificationIcon");
};
},
// 显示关联文件夹窗口
showAssociateFolderWindow: () => {
ipcRenderer.send("showClassificationAssociateFolderWindow");
},
// 关闭关联文件夹窗口
closeAssociateFolderWindow: () => {
ipcRenderer.send("closeClassificationAssociateFolderWindow");
},
// 设置关联文件夹
setAssociateFolder: (
id: number,
dir: string | null,
hiddenItems: string | null
): Classification => {
return ipcRenderer.sendSync("setClassificationAssociateFolder", {
id,
dir,
hiddenItems,
});
},
// 监听更新关联文件夹
onUpdateAssociateFolder: (callback): Function => {
ipcRenderer.on("onUpdateAssociateFolderClassification", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateAssociateFolderClassification");
};
},
// 是否拥有子分类
hasChildClassification: (id: number): boolean => {
return ipcRenderer.sendSync("hasChildClassification", id);
},
// 监听收起子分类
onCollapseSubClassification: (callback) => {
ipcRenderer.on("onCollapseSubClassification", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onCollapseSubClassification");
};
},
// 修改项目布局
onUpdateItemLayout: (callback) => {
ipcRenderer.on("onUpdateItemLayout", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItemLayout");
};
},
// 修改项目排序
onUpdateItemSort: (callback) => {
ipcRenderer.on("onUpdateItemSort", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItemSort");
};
},
// 修改项目列数
onUpdateItemColumnNumber: (callback) => {
ipcRenderer.on("onUpdateItemColumnNumber", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItemColumnNumber");
};
},
// 修改项目图标
onUpdateItemIconSize: (callback) => {
ipcRenderer.on("onUpdateItemIconSize", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItemIconSize");
};
},
// 根据文件夹创建分类
addClassificationByDirectory: (pathList: Array<string>) => {
ipcRenderer.send("addClassificationByDirectory", pathList);
},
// 监听根据文件夹创建分类
onAddClassificationByDirectory: (callback) => {
ipcRenderer.on("onAddClassificationByDirectory", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onAddClassificationByDirectory");
};
},
// 修改项目显示
onUpdateItemShowOnly: (callback) => {
ipcRenderer.on("onUpdateItemShowOnly", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItemShowOnly");
};
},
// 修改固定分类
onUpdateFixed: (callback) => {
ipcRenderer.on("onUpdateClassificationFixed", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateClassificationFixed");
};
},
// 将排序为打开次数的分类修改为默认排序
onUpdateItemOpenNumberSortToDefualt: (callback) => {
ipcRenderer.on("onUpdateItemOpenNumberSortToDefualt", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItemOpenNumberSortToDefualt");
};
},
// 显示聚合分类窗口
showAggregateWindow: () => {
ipcRenderer.send("showClassificationAggregateWindow");
},
// 关闭聚合分类窗口
closeAggregateWindow: () => {
ipcRenderer.send("closeClassificationAggregateWindow");
},
// 更新聚合分类
updateAggregate: (id: number, sort: string, itemCount: number): boolean => {
return ipcRenderer.sendSync("updateClassificationAggregate", {
id,
sort,
itemCount,
});
},
// 监听更新聚合分类
onUpdateAggregate: (callback) => {
ipcRenderer.on("onUpdateClassificationAggregate", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateClassificationAggregate");
};
},
// 监听修改排除搜索
onUpdateExcludeSearch: (callback) => {
ipcRenderer.on("onUpdateClassificationExcludeSearch", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateClassificationExcludeSearch");
};
},
});
contextBridge.exposeInMainWorld("item", {
// 显示新增/修改窗口
showAddEditWindow: () => {
ipcRenderer.send("showItemAddEditWindow");
},
// 关闭新增/修改窗口
closeAddEditWindow: () => {
ipcRenderer.send("closeItemAddEditWindow");
},
// 获取项目列表
list: (): Array<Item> => {
return ipcRenderer.sendSync("getItemList");
},
// 获取简单项目列表
simpleList: (): Array<Item> => {
return ipcRenderer.sendSync("getSimpleItemList");
},
// 根据ID查询分类
selectById: (id: number): Item | null => {
return ipcRenderer.sendSync("getItemById", { id });
},
// 新增项目
add: (item: Item): Item | null => {
return ipcRenderer.sendSync("addItem", item);
},
// 更新项目
update: (item: Item): boolean => {
return ipcRenderer.sendSync("updateItem", item);
},
// 项目排序
updateOrder: (
fromIdList: Array<number>,
toClassificationId: number,
newIndex: number | null
): boolean => {
return ipcRenderer.sendSync("updateItemOrder", {
fromIdList,
toClassificationId,
newIndex,
});
},
// 右键菜单
showRightMenu: (params: any) => {
ipcRenderer.send("showItemRightMenu", params);
},
// 创建网络图标窗口
createNetworkIconWindow: () => {
ipcRenderer.send("createItemNetworkIconWindow");
},
// 显示网络图标窗口
showNetworkIconWindow: () => {
ipcRenderer.send("showItemNetworkIconWindow");
},
// 关闭网络图标窗口
closeNetworkIconWindow: () => {
ipcRenderer.send("closeItemNetworkIconWindow");
},
// 监听网络图标
onNetworkIcon: (callback): Function => {
ipcRenderer.on("onItemNetworkIcon", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItemNetworkIcon");
};
},
// 创建SVG图标窗口
createSVGIconWindow: () => {
ipcRenderer.send("createItemSVGIconWindow");
},
// 显示SVG图标窗口
showSVGIconWindow: () => {
ipcRenderer.send("showItemSVGIconWindow");
},
// 关闭SVG图标窗口
closeSVGIconWindow: () => {
ipcRenderer.send("closeItemSVGIconWindow");
},
// 监听SVG图标
onSVGIcon: (callback): Function => {
ipcRenderer.on("onItemSVGIcon", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItemSVGIcon");
};
},
// 获取系统项目
getSystemItemList: () => {
ipcRenderer.send("getSystemItemList");
},
// 监听获取系统项目
onGetSystemItemList: (callback): Function => {
ipcRenderer.on("onGetSystemItemList", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onGetSystemItemList");
};
},
// 获取开始菜单项目
getStartMenuItemList: () => {
ipcRenderer.send("getStartMenuItemList");
},
// 监听获取开始菜单项目
onGetStartMenuItemList: (callback): Function => {
ipcRenderer.on("onGetStartMenuItemList", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onGetStartMenuItemList");
};
},
// 获取APPX项目
getAppxItemList: () => {
ipcRenderer.send("getAppxItemList");
},
// 监听APPX项目
onGetAppxItemList: (callback): Function => {
ipcRenderer.on("onGetAppxItemList", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onGetAppxItemList");
};
},
// 监听新增项目
onAdd: (callback): Function => {
ipcRenderer.on("onAddItem", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onAddItem");
};
},
// 监听更新项目
onUpdate: (callback): Function => {
ipcRenderer.on("onUpdateItem", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateItem");
};
},
// 监听删除项目
onDelete: (callback): Function => {
ipcRenderer.on("onDeleteItem", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onDeleteItem");
};
},
// 监听锁定/解锁项目
onLock: (callback): Function => {
ipcRenderer.on("onLockItem", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onLockItem");
};
},
// 拖入项目
drop: (classificationId: number, pathList: Array<string>) => {
ipcRenderer.send("dropItem", {
classificationId,
pathList,
});
},
// 监听批量操作
onBatchOperation: (callback): Function => {
ipcRenderer.on("onItemBatchOperation", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItemBatchOperation");
};
},
// 运行项目
run: (type: string, operation: string, item: Item) => {
ipcRenderer.send("runItem", { operation, item, type });
},
// 监听转换路径
onConvertPath: (callback): Function => {
ipcRenderer.on("onConvertPath", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onConvertPath");
};
},
// 监听刷新图标
onRefreshIcon: (callback): Function => {
ipcRenderer.on("onRefreshItemIcon", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onRefreshItemIcon");
};
},
// 监听移动项目
onMove: (callback): Function => {
ipcRenderer.on("onMoveItem", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onMoveItem");
};
},
// 监听批量操作全选
onBatchOperationSelectAll: (callback): Function => {
ipcRenderer.on("onItembatchOperationSelectAll", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItembatchOperationSelectAll");
};
},
// 监听项目右键菜单关闭
onRightMenuClose: (callback): Function => {
ipcRenderer.on("onItemRightMenuClose", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItemRightMenuClose");
};
},
// 监听项目资源管理器菜单
onExplorerMenu: (callback): Function => {
ipcRenderer.on("onItemExplorerMenu", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItemExplorerMenu");
};
},
// 项目拖出
dragOut: (item: Item) => {
ipcRenderer.send("itemDragOut", item);
},
// 取消项目拖出
onCancelDragOut: (callback): Function => {
ipcRenderer.on("onItemCancelDragOut", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onItemCancelDragOut");
};
},
// 监听更新打开信息
onUpdateOpenInfo: (callback): Function => {
ipcRenderer.on("onUpdateOpenInfo", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateOpenInfo");
};
},
// 监听无效项目
onCheckInvalid: (callback): Function => {
ipcRenderer.on("onCheckInvalidItem", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onCheckInvalidItem");
};
},
// 更新打开信息
updateOpenInfo: (type: string, id: number) => {
ipcRenderer.send("updateItemOpenInfo", { type, id });
},
});
contextBridge.exposeInMainWorld("setting", {
// 创建设置窗口
createWindow: () => {
ipcRenderer.send("createSettingWindow");
},
// 显示设置窗口
showWindow: () => {
ipcRenderer.send("showSettingWindow");
},
// 关闭设置窗口
closeWindow: () => {
ipcRenderer.send("closeSettingWindow");
},
// 查询设置
select: (): Setting | null => {
return ipcRenderer.sendSync("selectSetting");
},
// 新增设置
add: (setting: Setting): boolean => {
return ipcRenderer.sendSync("addSetting", setting);
},
// 更新设置
update: (setting: Setting): boolean => {
return ipcRenderer.sendSync("updateSetting", setting);
},
// 监听更新设置
onUpdate: (callback): Function => {
ipcRenderer.on("onUpdateSetting", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onUpdateSetting");
};
},
// 开机启动
setStartup: (value: boolean) => {
ipcRenderer.send("startup", value);
},
// 隐藏托盘图标
setTray: (show: boolean) => {
ipcRenderer.send("setTray", show);
},
// 设置快捷键
setShortcutKey: (setting: Setting) => {
ipcRenderer.send("setShortcutKey", setting);
},
// 永远置顶
setAlwaysTop: (value: boolean) => {
ipcRenderer.send("setAlwaysTop", value);
},
// 锁定尺寸
setLockSize: (value: boolean) => {
ipcRenderer.send("setLockSize", value);
},
// 固定位置
setFixedPosition: (fixedPosition: boolean, alwaysCenter: boolean) => {
ipcRenderer.send("setFixedPosition", {
fixedPosition,
alwaysCenter,
});
},
// 永远居中
setAlwaysCenter: (fixedPosition: boolean, alwaysCenter: boolean) => {
ipcRenderer.send("setAlwaysCenter", {
fixedPosition,
alwaysCenter,
});
},
// 边缘吸附
setEdgeAdsorb: (value: boolean) => {
ipcRenderer.send("setEdgeAdsorb", value);
},
// 上传背景图
uploadBackgrounImage: (): any => {
return ipcRenderer.sendSync("uploadBackgrounImage");
},
// 监听设置背景图
onSetBacngroundImage: (callback) => {
ipcRenderer.on("onSetBacngroundImage", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onSetBacngroundImage");
};
},
// 获取背景图
getBackgroundImage: (name: string, windowName: string) => {
ipcRenderer.send("getBackgroundImage", { name, windowName });
},
// 检测无效项目
setCheckInvalidItem: (value: boolean) => {
ipcRenderer.send("setCheckInvalidItem", value);
},
// 项目打开次数
setOpenNumber: (value: boolean) => {
ipcRenderer.send("setItemOpenNumber", value);
},
});
contextBridge.exposeInMainWorld("quickSearch", {
// 初始化完毕
initFinished: () => {
ipcRenderer.send("quickSearchInitFinished");
},
// 显示窗口
showWindow: () => {
ipcRenderer.send("showQuickSearchWindow");
},
// 隐藏窗口
hideWindow: () => {
ipcRenderer.send("hideQuickSearchWindow");
},
// 显示窗口之前
onShowWindowBefore: (callback): Function => {
ipcRenderer.on("onShowQuickSearchWindowBefore", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onShowQuickSearchWindowBefore");
};
},
// 清空数据
onClearData: (callback): Function => {
ipcRenderer.on("onQuickSearchClearData", (event, data) => {
callback(data);
});
return () => {
ipcRenderer.removeAllListeners("onQuickSearchClearData");
};
},
// 设置窗口高度
setWindowHeight: (height: number) => {
ipcRenderer.send("setQuickSearchWindowHeight", height);
},
});
contextBridge.exposeInMainWorld("about", {
// 创建窗口
createWindow: () => {
ipcRenderer.send("createAboutWindow");
},
// 显示窗口
showWindow: () => {
ipcRenderer.send("showAboutWindow");
},
// 关闭窗口
closeWindow: () => {
ipcRenderer.send("closeAboutWindow");
},
});
contextBridge.exposeInMainWorld("data", {
// 创建备份/恢复数据窗口
createBackupRestoreDataWindow: () => {
ipcRenderer.send("createBackupRestoreDataWindow");
},
// 显示备份/恢复数据窗口
showBackupRestoreDataWindow: () => {
ipcRenderer.send("showBackupRestoreDataWindow");
},
// 关闭备份/恢复数据窗口
closeBackupRestoreDataWindow: () => {
ipcRenderer.send("closeBackupRestoreDataWindow");
},
// 备份数据
backupData: () => {
ipcRenderer.send("backupData");
},
// 恢复数据
restoreData: () => {
ipcRenderer.send("restoreData");
},
});

75
electron/types/global.d.ts vendored Normal file
View File

@ -0,0 +1,75 @@
import { BrowserWindow, Tray } from "electron";
import { FSWatcher } from "node:fs";
import { Setting } from "../../types/setting";
declare global {
// addon
var addon: any;
// 语言
var language: any;
// 主窗口
var mainWindow: BrowserWindow | null;
// 快速搜索窗口
var quickSearchWindow: BrowserWindow | null;
// 快速搜索窗口是否初始化完成
var quickSearchWindowInit: Boolean | null;
// 设置窗口
var settingWindow: BrowserWindow | null;
// 分类添加/编辑窗口
var classificationAddEditWindow: BrowserWindow | null;
// 分类图标窗口
var classificationSetIconWindow: BrowserWindow | null;
// 关联分类窗口
var classificationAssociateFolderWindow: BrowserWindow | null;
// 聚合分类窗口
var classificationAggregateWindow: BrowserWindow | null;
// 项目添加/编辑窗口
var itemAddEditWindow: BrowserWindow | null;
// 项目网络图标窗口
var itemNetworkIconWindow: BrowserWindow | null;
// 项目SVG图标窗口
var itemSVGIconWindow: BrowserWindow | null;
// 关于窗口
var aboutWindow: BrowserWindow | null;
// 备份/恢复数据窗口
var backupRestoreDataWindow: BrowserWindow | null;
// 存储关联分类监听
var associateFolderWatcher: Map<number, AssociateFolderData>;
// 设置
var setting: Setting | null;
// 托盘
var tray: Tray;
// 主窗口方向
var mainWindowDirection: String | null;
// 停靠在桌面边缘时自动隐藏timer
var autoHideTimer: NodeJS.Timeout;
// 需要失去焦点时隐藏
var blurHide: boolean | null;
// 双击任务栏显示/隐藏timer
var doubleClickTaskbarTimer: NodeJS.Timeout;
// 双击任务栏显示/隐藏counter
var doubleClickTaskbarCounter: number;
// 监测无效项目interval
var checkInvalidItemInterval: NodeJS.Timeout;
// 存储子进程信息
var childProcessMap: Map<number, ChildProcessInfo>;
// 分类右键菜单显示
var classificationRightMenu: boolean | null;
// 项目右键菜单显示
var itemRightMenu: boolean | null;
}
export interface ChildProcessInfo {
utilityProcess: Electron.UtilityProcess;
port1: Electron.MessagePortMain;
port2: Electron.MessagePortMain;
}
export interface AssociateFolderData {
classificationId: number;
dir: string;
hiddenItems: string | null;
watch: FSWatcher;
}
export {};

BIN
images/soft1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
images/soft2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
images/soft3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
images/soft4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
images/soft5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
images/soft6.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
images/soft7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
images/soft8.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 636 KiB

16
index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<title>Dawn Launcher</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

View File

@ -1,60 +1,73 @@
{ {
"name": "DawnLauncher", "name": "dawn-launcher",
"productName": "Dawn Launcher", "productName": "Dawn Launcher",
"author": "Dawn Launcher", "version": "1.3.1",
"version": "1.2.3", "main": "dist-electron/main/index.js",
"description": "Windows 快捷启动工具,帮助您整理杂乱无章的桌面,分门别类管理您的桌面快捷方式,让您的桌面保持干净整洁。",
"author": "FanChenIO",
"private": true, "private": true,
"gypfile": true, "keywords": [
"scripts": { "electron",
"serve": "vue-cli-service serve", "vite",
"build": "vue-cli-service build", "vue"
"electron:build": "vue-cli-service electron:build", ],
"electron:serve": "vue-cli-service electron:serve", "debug": {
"postinstall": "electron-builder install-app-deps", "env": {
"postuninstall": "electron-builder install-app-deps" "VITE_DEV_SERVER_URL": "http://127.0.0.1:3344/"
}
}, },
"dependencies": { "scripts": {
"@ckpack/vue-color": "^1.4.1", "dev": "vite",
"bindings": "^1.5.0", "build": "vue-tsc --noEmit && vite build && electron-builder",
"cheerio": "^1.0.0-rc.12", "preview": "vite preview",
"core-js": "^3.8.3", "rsbuild": "napi build --release --strip ./native",
"dompurify": "^3.0.3", "rebuild": "electron rebuild.js",
"electron-store": "^8.1.0", "postinstall": "yarn run rebuild && yarn run rsbuild"
"jimp": "^0.22.7", },
"mime": "^3.0.0", "napi": {
"node-addon-api": "^5.0.0", "name": "addon"
"pinyin-match": "^1.2.4",
"request": "^2.88.2",
"retry": "^0.13.1",
"simplebar": "^5.3.8",
"sortablejs": "^1.15.0",
"urijs": "^1.19.11",
"uuid": "^9.0.0",
"vue": "^3.3.4",
"vue-router": "^4.2.0",
"vuex": "^4.1.0",
"xml2js": "^0.5.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.16", "@napi-rs/cli": "^2.16.3",
"@tailwindcss/forms": "^0.5.3", "@vicons/material": "^0.12.0",
"@vue/cli-plugin-babel": "~5.0.0", "@vicons/utils": "^0.1.4",
"@vue/cli-service": "~5.0.0", "@vitejs/plugin-vue": "^4.4.0",
"autoprefixer": "^10.4.8", "autoprefixer": "^10.4.16",
"electron": "^22.3.17", "better-sqlite3-multiple-ciphers": "^9.0.0",
"electron-builder": "^24.4.0", "electron": "^26.4.2",
"electron-devtools-installer": "^3.2.0", "electron-builder": "^24.6.4",
"modclean": "^3.0.0-beta.1", "less": "^4.2.0",
"postcss": "^8.4.16", "less-loader": "^11.1.3",
"prettier": "^2.7.1", "naive-ui": "^2.35.0",
"tailwindcss": "^3.1.8", "postcss": "^8.4.31",
"terser-webpack-plugin": "^5.3.8", "tailwindcss": "^3.3.5",
"vue-cli-plugin-electron-builder": "^3.0.0-alpha.4" "typescript": "^5.2.2",
"vite": "^4.4.11",
"vite-plugin-electron": "^0.14.1",
"vue": "^3.3.7",
"vue-tsc": "^1.8.22"
}, },
"browserslist": [ "dependencies": {
"> 1%", "@types/dompurify": "^3.0.3",
"last 2 versions", "@types/mime": "^3.0.2",
"not dead", "@types/request": "^2.48.10",
"not ie 11" "@types/retry": "^0.12.3",
] "@types/sortablejs": "^1.15.3",
"@types/urijs": "^1.19.22",
"@types/xml2js": "^0.4.12",
"cheerio": "1.0.0-rc.12",
"dompurify": "^3.0.6",
"electron-log": "^5.0.0",
"electron-store": "^8.1.0",
"mime": "^3.0.0",
"pinia": "^2.1.7",
"pinyin-pro": "^3.17.0",
"request": "^2.88.2",
"retry": "^0.13.1",
"simplebar": "^6.2.5",
"sortablejs": "^1.15.0",
"urijs": "^1.19.11",
"vue-router": "^4.2.5",
"xml2js": "^0.6.2"
}
} }

View File

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 440 KiB

After

Width:  |  Height:  |  Size: 440 KiB

View File

Before

Width:  |  Height:  |  Size: 428 KiB

After

Width:  |  Height:  |  Size: 428 KiB

33
rebuild.js Normal file
View File

@ -0,0 +1,33 @@
const path = require("path");
const child = require("child_process");
// If you prefer electron-rebuild:
// 👉 https://github.com/WiseLibs/better-sqlite3/blob/v8.5.2/docs/troubleshooting.md#electron
// 👉 https://stackoverflow.com/questions/46384591/node-was-compiled-against-a-different-node-js-version-using-node-module-versio/52796884#52796884
const better_sqlite3 = require.resolve("better-sqlite3-multiple-ciphers");
const better_sqlite3_root = path.posix.join(
better_sqlite3.slice(0, better_sqlite3.lastIndexOf("node_modules")),
"node_modules/better-sqlite3-multiple-ciphers"
);
const cp = child.spawn(
process.platform === "win32" ? "npm.cmd" : "npm",
[
"run",
"build-release",
`--target=${process.versions.electron}`,
// https://github.com/electron/electron/blob/v26.1.0/docs/tutorial/using-native-node-modules.md#manually-building-for-electron
"--dist-url=https://electronjs.org/headers",
],
{
cwd: better_sqlite3_root,
stdio: "inherit",
}
);
cp.on("exit", (code) => {
if (code === 0) {
console.log("Rebuild better-sqlite3 success.");
}
process.exit(code);
});

167
rust/lib.rs Normal file
View File

@ -0,0 +1,167 @@
use napi::JsFunction;
use napi_derive::napi;
use std::collections::HashMap;
mod windows;
/**
*
*/
#[allow(dead_code)]
#[napi]
fn get_file_icon(path: String) -> Option<String> {
windows::get_file_icon(&path)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn search_path(path: String) -> Option<String> {
windows::search_path(&path)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn get_shortcut_file_info(path: String) -> Option<HashMap<String, String>> {
windows::get_shortcut_file_info(&path)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn shell_execute(operation: String, file: String, params: String, start_location: Option<String>) {
windows::shell_execute(operation, file, params, start_location)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn system_item_execute(target: String, params: Option<String>) {
windows::system_item_execute(&target, params.as_deref())
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn open_file_location(path: String) {
windows::open_file_location(&path)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn explorer_context_menu(window: i32, path: String, x: i32, y: i32) {
windows::explorer_context_menu(window, &path, x, y)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn get_env_by_name(name: String) -> Option<String> {
windows::get_env_by_name(&name)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn is_fullscreen() -> bool {
windows::is_fullscreen()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn switch_english(window: i32) {
windows::switch_english(window)
}
/**
* HOOK
*/
#[allow(dead_code)]
#[napi]
fn create_mouse_hook(callback: JsFunction) {
windows::create_mouse_hook(callback)
}
/**
* HOOK
*/
#[allow(dead_code)]
#[napi]
fn enable_mouse_hook() {
windows::enable_mouse_hook()
}
/**
* HOOK
*/
#[allow(dead_code)]
#[napi]
fn disable_mouse_hook() {
windows::disable_mouse_hook()
}
/**
* ClassName
*/
#[allow(dead_code)]
#[napi]
pub fn get_cursor_pos_window_class_name() -> String {
windows::get_cursor_pos_window_class_name()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
pub fn get_clipboard_file_list() -> Vec<String> {
windows::get_clipboard_file_list()
}
/**
* BITMAP
*/
#[allow(dead_code)]
#[napi]
pub fn clipboard_has_bitmap() -> bool {
windows::clipboard_has_bitmap()
}
/**
* BITMAP的BASE64
*/
#[allow(dead_code)]
#[napi]
pub fn get_clipboard_bitmap_base64() -> Option<String> {
windows::get_clipboard_bitmap_base64()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
pub fn empty_recycle_bin(window: i32) {
windows::empty_recycle_bin(window)
}

673
rust/windows.rs Normal file
View File

@ -0,0 +1,673 @@
use base64::{engine::general_purpose, Engine as _};
use clipboard_win::{formats, get_clipboard};
use image::{imageops::flip_vertical, ImageBuffer, ImageFormat, Rgba};
use napi::{
threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode},
JsFunction,
};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
io::Cursor,
path::Path,
process::Command,
sync::atomic::{AtomicBool, Ordering},
};
use windows::{
core::{ComInterface, HSTRING, PCSTR, PCWSTR},
w,
Win32::{
Foundation::{HWND, LPARAM, LRESULT, MAX_PATH, POINT, RECT, SIZE, WPARAM},
Graphics::Gdi::{
GetMonitorInfoW, GetObjectW, MonitorFromWindow, BITMAP, MONITORINFO,
MONITOR_DEFAULTTONEAREST,
},
Storage::FileSystem::SearchPathW,
System::{
Com::{
CoCreateInstance, CoInitializeEx, CoUninitialize, IPersistFile,
CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED, STGM_READ,
},
Environment::GetEnvironmentVariableW,
SystemInformation::GetSystemDirectoryW,
},
UI::{
Input::Ime::{
ImmGetContext, ImmReleaseContext, ImmSetConversionStatus, IME_CMODE_ALPHANUMERIC,
IME_SMODE_AUTOMATIC,
},
Shell::{
BHID_SFUIObject, IContextMenu, IShellItem, IShellItemImageFactory, IShellLinkW,
SHCreateItemFromParsingName, SHEmptyRecycleBinW, SHQueryUserNotificationState,
ShellExecuteW, ShellLink, CMF_NORMAL, CMINVOKECOMMANDINFO,
QUNS_ACCEPTS_NOTIFICATIONS, QUNS_APP, QUNS_BUSY, QUNS_NOT_PRESENT,
QUNS_PRESENTATION_MODE, QUNS_QUIET_TIME, QUNS_RUNNING_D3D_FULL_SCREEN,
SHERB_NOSOUND, SIIGBF_ICONONLY, SLGP_UNCPRIORITY,
},
WindowsAndMessaging::{
CallNextHookEx, CreatePopupMenu, DestroyMenu, FindWindowW, GetClassNameW,
GetCursorPos, GetForegroundWindow, GetSystemMetrics, GetWindowRect, SendMessageW,
SetForegroundWindow, SetWindowsHookExW, TrackPopupMenu, WindowFromPoint, HHOOK,
MSLLHOOKSTRUCT, SC_MONITORPOWER, SM_CXSCREEN, SM_CYSCREEN, SW_NORMAL,
SW_SHOWDEFAULT, TPM_NONOTIFY, TPM_RETURNCMD, WH_MOUSE_LL, WM_LBUTTONDOWN,
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
WM_MOUSEWHEEL, WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SYSCOMMAND,
},
},
},
};
// 获取图标并转为BASE64
pub fn get_file_icon(path: &str) -> Option<String> {
// 返回信息
let mut base64 = None;
// HSTRING
let path = HSTRING::from(path);
// PCWSTR
let path: PCWSTR = PCWSTR(path.as_ptr());
// unsafe
unsafe {
// Init
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
}
// IShellItemImageFactory
let result = unsafe {
SHCreateItemFromParsingName::<PCWSTR, Option<_>, IShellItemImageFactory>(path, None)
};
if let Ok(shell_item_image_factory) = result {
if let Some(mut image_buffer) = get_file_icon_image_buffer(&shell_item_image_factory, 256) {
// 判断像素点,是否是小图标
let mut transparency: f64 = 0_f64;
let mut non_transparency: f64 = 0_f64;
for y in 0..image_buffer.height() {
for x in 0..image_buffer.width() {
let pixel = image_buffer.get_pixel(x, y);
let alpha = pixel[3]; // 获取像素的 Alpha 通道值
if alpha == 0 {
// 透明
transparency += 1_f64;
} else {
// 不透明
non_transparency += 1_f64;
}
}
}
// 计算如果透明区域大于百分之70就代表是小图标
let proportion =
(transparency / (transparency + non_transparency) * 100_f64).round() as u32;
if proportion >= 70 {
// 获取小图标
if let Some(image_buffer_small) =
get_file_icon_image_buffer(&shell_item_image_factory, 48)
{
image_buffer = image_buffer_small;
}
}
// 翻转图片
image_buffer = flip_vertical(&image_buffer);
// 转码
base64 = Some(image_buffer_to_base64(image_buffer));
}
}
unsafe {
// UnInit
CoUninitialize();
}
base64
}
// 获取图标并转为ImageBuffer
fn get_file_icon_image_buffer(
shell_item_image_factory: &IShellItemImageFactory,
size: i32,
) -> Option<ImageBuffer<Rgba<u8>, Vec<u8>>> {
// 获取图片
let result =
unsafe { shell_item_image_factory.GetImage(SIZE { cx: size, cy: size }, SIIGBF_ICONONLY) };
if let Ok(h_bitmap) = result {
// 转为BITMAP
let mut bitmap: BITMAP = BITMAP::default();
unsafe {
GetObjectW(
h_bitmap,
std::mem::size_of::<BITMAP>() as i32,
Some(&mut bitmap as *mut _ as _),
);
}
// 转换ImageBuffer
let width: u32 = bitmap.bmWidth as u32;
let height = bitmap.bmHeight as u32;
let pixel_data: &[u8] = unsafe {
std::slice::from_raw_parts(bitmap.bmBits as *const u8, (width * height * 4) as usize)
};
let result = ImageBuffer::<Rgba<u8>, _>::from_raw(width, height, pixel_data.to_vec());
if let Some(mut image_buffer) = result {
// 将ImageBuffer的颜色通道顺序从BGRA转为RGB
for pixel in image_buffer.pixels_mut() {
let b = pixel[0];
let r = pixel[2];
pixel[0] = r;
pixel[2] = b;
}
return Some(image_buffer);
}
}
None
}
/**
* imageBuffer转BASE64
*/
fn image_buffer_to_base64(image_buffer: ImageBuffer<Rgba<u8>, Vec<u8>>) -> String {
// imageBufferData
let mut image_buffer_data = Cursor::new(Vec::new());
// write
image_buffer
.write_to(&mut image_buffer_data, ImageFormat::Png)
.unwrap();
// 转码
format!(
"data:image/png;base64,{}",
general_purpose::STANDARD.encode(image_buffer_data.into_inner())
)
}
/**
*
*/
pub fn shell_execute(
operation: String,
file: String,
params: String,
start_location: Option<String>,
) {
// dir
let dir = start_location.unwrap_or_else(|| {
// 判断是否是文件夹
let path = Path::new(&file);
if path.is_dir() {
// 文件夹
file.clone()
} else {
// 文件 获取上一级目录
path.parent().unwrap().display().to_string()
}
});
// HSTRING
let operation = HSTRING::from(operation.as_str());
let file = HSTRING::from(file.as_str());
let params = HSTRING::from(params.as_str());
let dir = HSTRING::from(dir.as_str());
// PCWSTR
let operation = PCWSTR(operation.as_ptr());
let file = PCWSTR(file.as_ptr());
let params = PCWSTR(params.as_ptr());
let dir = PCWSTR(dir.as_ptr());
unsafe {
// execute
ShellExecuteW(None, operation, file, params, dir, SW_SHOWDEFAULT);
}
}
/**
*
*/
pub fn get_shortcut_file_info(path: &str) -> Option<HashMap<String, String>> {
// HSTRING
let path = HSTRING::from(path);
unsafe {
// Init
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
}
// IShellLinkW
let shell_link_result: Result<IShellLinkW, windows::core::Error> =
unsafe { CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER) };
if let Ok(shell_link) = shell_link_result {
// IPersistFile
let persist_file_result: Result<IPersistFile, windows::core::Error> = shell_link.cast();
if let Ok(persist_file) = persist_file_result {
let load_result = unsafe {
// 加载路径
persist_file.Load(PCWSTR(path.as_ptr()), STGM_READ)
};
if let Ok(()) = load_result {
// 获取目标
let mut target_buffer = [0u16; MAX_PATH as usize];
let _ = unsafe {
shell_link.GetPath(
&mut target_buffer,
std::ptr::null_mut(),
SLGP_UNCPRIORITY.0 as u32,
)
};
// 获取参数
let mut arguments_buffer = [0u16; MAX_PATH as usize];
let _ = unsafe { shell_link.GetArguments(&mut arguments_buffer) };
// map
let mut map = HashMap::with_capacity(2);
map.insert(String::from("target"), u16_to_string(&target_buffer));
map.insert(String::from("arguments"), u16_to_string(&arguments_buffer));
return Some(map);
}
}
}
unsafe {
// UnInit
CoUninitialize();
}
None
}
/**
*
*/
pub fn system_item_execute(target: &str, params: Option<&str>) {
if target == "static:TurnOffMonitor" {
// 关闭显示器
turn_off_monitor()
} else {
let mut file = target.to_string();
if !target.starts_with("shell:") {
// 如果不是shell开头就查询路径
file = search_path(target).unwrap_or(target.to_string());
}
let file = HSTRING::from(file);
let params = match params {
Some(p) => HSTRING::from(p),
_ => HSTRING::new(),
};
// 获取系统盘路径当作工作目录
let mut buffer = [0u16; MAX_PATH as usize];
unsafe {
GetSystemDirectoryW(Some(&mut buffer));
}
let dir = u16_to_string(&buffer);
let dir = HSTRING::from(dir);
// execute
unsafe {
ShellExecuteW(
None,
w!("open"),
PCWSTR(file.as_ptr()),
PCWSTR(params.as_ptr()),
PCWSTR(dir.as_ptr()),
SW_SHOWDEFAULT,
);
}
}
}
/**
*
*/
pub fn turn_off_monitor() {
unsafe {
let _ = SendMessageW(
FindWindowW(PCWSTR::null(), PCWSTR::null()),
WM_SYSCOMMAND,
WPARAM(SC_MONITORPOWER as usize),
LPARAM(2),
);
}
}
/**
*
*/
pub fn open_file_location(path: &str) {
let _ = Command::new("explorer").arg("/select,").arg(path).spawn();
}
/**
*
*/
pub fn explorer_context_menu(window: i32, path: &str, x: i32, y: i32) {
// IShellItem
let path = HSTRING::from(path);
if let Ok(shell_item) =
unsafe { SHCreateItemFromParsingName::<_, _, IShellItem>(PCWSTR(path.as_ptr()), None) }
{
// IContextMenu
if let Ok(context_menu) =
unsafe { shell_item.BindToHandler::<_, IContextMenu>(None, &BHID_SFUIObject) }
{
// Menu
if let Ok(menu) = unsafe { CreatePopupMenu() } {
// 写入菜单
if let Ok(()) =
unsafe { context_menu.QueryContextMenu(menu, 0, 1, 0x7FFF, CMF_NORMAL) }
{
// HWND
let hwnd = HWND(window as isize);
// 弹出菜单
let res = unsafe {
SetForegroundWindow(hwnd);
TrackPopupMenu(menu, TPM_RETURNCMD | TPM_NONOTIFY, x, y, 0, hwnd, None)
};
unsafe {
DestroyMenu(menu);
}
if res.as_bool() {
let mut info = CMINVOKECOMMANDINFO::default();
info.cbSize = std::mem::size_of::<CMINVOKECOMMANDINFO>() as u32;
info.hwnd = hwnd;
info.lpVerb = PCSTR((res.0 - 1) as *mut u8);
info.nShow = SW_NORMAL.0 as i32;
let _ = unsafe { context_menu.InvokeCommand(&info) };
}
}
}
}
}
}
/**
*
*/
pub fn search_path(name: &str) -> Option<String> {
let name = HSTRING::from(name);
let mut buffer = [0u16; MAX_PATH as usize];
let result = unsafe {
SearchPathW(
PCWSTR::null(),
PCWSTR(name.as_ptr()),
PCWSTR::null(),
Some(&mut buffer),
None,
)
};
if result > 0 {
Some(u16_to_string(&buffer))
} else {
None
}
}
/**
* String
*/
fn u16_to_string(slice: &[u16]) -> String {
let mut vec = vec![];
for s in slice {
if *s > 0 {
vec.push(*s);
}
}
String::from_utf16_lossy(&vec)
}
/**
*
*/
pub fn get_env_by_name(name: &str) -> Option<String> {
let name = HSTRING::from(name);
let mut buffer = [0u16; MAX_PATH as usize];
let result = unsafe { GetEnvironmentVariableW(PCWSTR(name.as_ptr()), Some(&mut buffer)) };
if result > 0 {
Some(u16_to_string(&buffer))
} else {
None
}
}
/**
*
*/
fn is_fullscreen_window() -> bool {
// 获取当前活动窗口的句柄
let foreground_window = unsafe { GetForegroundWindow() };
// 获取活动窗口的位置信息
let mut window_rect = RECT::default();
unsafe { GetWindowRect(foreground_window, &mut window_rect) };
// 获取包含活动窗口的显示器句柄
let monitor = unsafe { MonitorFromWindow(foreground_window, MONITOR_DEFAULTTONEAREST) };
// 获取显示器信息
let mut monitor_info = MONITORINFO::default();
monitor_info.cbSize = std::mem::size_of::<MONITORINFO>() as u32;
unsafe { GetMonitorInfoW(monitor, &mut monitor_info) };
// 获取屏幕的尺寸
let screen_width = unsafe { GetSystemMetrics(SM_CXSCREEN) };
let screen_height = unsafe { GetSystemMetrics(SM_CYSCREEN) };
// 比较窗口位置和显示器尺寸来判断是否处于全屏模式
if window_rect.left <= 0
&& window_rect.top <= 0
&& window_rect.right >= screen_width
&& window_rect.bottom >= screen_height
&& monitor_info.rcMonitor.left == 0
&& monitor_info.rcMonitor.top == 0
&& monitor_info.rcMonitor.right == screen_width
&& monitor_info.rcMonitor.bottom == screen_height
{
// 获取窗口类名
let mut buffer = [0u16; MAX_PATH as usize];
unsafe { GetClassNameW(foreground_window, &mut buffer) };
// 转为String
let name = u16_to_string(&buffer);
if name != "WorkerW" {
return true;
}
}
false
}
/**
*
*/
pub fn is_fullscreen() -> bool {
if let Ok(state) = unsafe { SHQueryUserNotificationState() } {
if state == QUNS_NOT_PRESENT {
// 非全屏(机器锁定/屏幕保护程序/用户切换)
return false;
} else if state == QUNS_BUSY {
// 全屏F11 全屏,我试过的所有视频游戏都使用它)
return is_fullscreen_window();
} else if state == QUNS_RUNNING_D3D_FULL_SCREEN {
// 全屏Direct3D 应用程序以独占模式运行,即全屏)
return true;
} else if state == QUNS_PRESENTATION_MODE {
// 全屏(一种用于显示全屏演示文稿的特殊模式)
return true;
} else if state == QUNS_ACCEPTS_NOTIFICATIONS {
// 不是全屏
return false;
} else if state == QUNS_QUIET_TIME {
// 不是全屏
return false;
} else if state == QUNS_APP {
// 不是全屏
return false;
}
}
false
}
/**
*
*/
pub fn switch_english(window: i32) {
// 窗口句柄
let hwnd = HWND(window as isize);
// 获取输入法上下文
let imc = unsafe { ImmGetContext(hwnd) };
// 设置输入法的首选转换模式为英文
unsafe { ImmSetConversionStatus(imc, IME_CMODE_ALPHANUMERIC, IME_SMODE_AUTOMATIC) };
// 释放输入法上下文
unsafe { ImmReleaseContext(hwnd, imc) };
}
// 是否回调
static mut MOUSE_HOOK_CALL: AtomicBool = AtomicBool::new(false);
// ThreadsafeFunction
static mut TSFN: Option<ThreadsafeFunction<String>> = None;
// 全局鼠标HOOK
static mut MOUSE_HOOK: Option<HHOOK> = None;
// 鼠标事件
#[derive(Debug, Serialize, Deserialize)]
struct MouseEvent {
event: String,
x: i32,
y: i32,
button: i32,
mouse_data: u32,
}
/**
* HOOK回调方法
*/
unsafe extern "system" fn mouse_proc(code: i32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
if code >= 0 && MOUSE_HOOK_CALL.load(Ordering::Relaxed) {
// 鼠标坐标
let msll_struct = lparam.0 as *const MSLLHOOKSTRUCT;
let x = (*msll_struct).pt.x;
let y = (*msll_struct).pt.y;
let mouse_data = (*msll_struct).mouseData;
// 参数
let param = wparam.0 as u32;
// 事件
let mut event = String::from("");
// 按键类型
let mut button = -1;
// 判断事件
if param == WM_MOUSEMOVE {
// 鼠标移动
event.push_str("mousemove");
} else {
// 鼠标操作
if param == WM_LBUTTONUP || param == WM_RBUTTONUP || param == WM_MBUTTONUP {
event.push_str("mouseup");
} else if param == WM_LBUTTONDOWN || param == WM_RBUTTONDOWN || param == WM_MBUTTONDOWN
{
event.push_str("mousedown");
} else if param == WM_MOUSEWHEEL || param == WM_MOUSEHWHEEL {
event.push_str("mousewheel");
}
// 按键类型
if param == WM_LBUTTONUP || param == WM_LBUTTONDOWN {
button = 1;
} else if param == WM_RBUTTONUP || param == WM_RBUTTONDOWN {
button = 2;
} else if param == WM_MBUTTONUP || param == WM_MBUTTONDOWN {
button = 3;
} else if param == WM_MOUSEWHEEL {
button = 0;
} else if param == WM_MOUSEHWHEEL {
button = 1;
}
}
if event != "" {
if let Some(func) = TSFN.as_ref() {
let mouse_event = MouseEvent {
event,
x,
y,
mouse_data,
button,
};
func.call(
Ok(serde_json::to_string(&mouse_event).unwrap()),
ThreadsafeFunctionCallMode::NonBlocking,
);
}
}
}
return CallNextHookEx(MOUSE_HOOK.unwrap(), code, wparam, lparam);
}
/**
* hook
*/
pub fn create_mouse_hook(callback: JsFunction) {
// 创建回调
if let Ok(threadsafe_function) =
callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))
{
unsafe { TSFN = Some(threadsafe_function) };
// 创建鼠标HOOK
if let Ok(hook) = unsafe { SetWindowsHookExW(WH_MOUSE_LL, Some(mouse_proc), None, 0) } {
unsafe {
MOUSE_HOOK = Some(hook);
MOUSE_HOOK_CALL.store(true, Ordering::Relaxed);
};
}
}
}
/**
* HOOK
*/
pub fn enable_mouse_hook() {
unsafe { MOUSE_HOOK_CALL.store(true, Ordering::Relaxed) }
}
/**
* HOOK
*/
pub fn disable_mouse_hook() {
unsafe { MOUSE_HOOK_CALL.store(false, Ordering::Relaxed) }
}
/**
* ClassName
*/
pub fn get_cursor_pos_window_class_name() -> String {
// 获取鼠标位置
let mut point: POINT = POINT::default();
unsafe {
GetCursorPos(&mut point);
}
// 获取鼠标所在的窗口句柄
let hwnd = unsafe { WindowFromPoint(point) };
// 获取窗口的ClassName
let mut buffer = [0u16; MAX_PATH as usize];
unsafe {
GetClassNameW(hwnd, &mut buffer);
};
// 返回
u16_to_string(&buffer)
}
/**
*
*/
pub fn get_clipboard_file_list() -> Vec<String> {
match get_clipboard(formats::FileList) {
Ok(vec) => vec,
Err(_) => vec![],
}
}
/**
* BITMAP
*/
pub fn clipboard_has_bitmap() -> bool {
match get_clipboard(formats::Bitmap) {
Ok(_) => true,
Err(_) => false,
}
}
/**
* BITMAP的BASE64
*/
pub fn get_clipboard_bitmap_base64() -> Option<String> {
match get_clipboard(formats::Bitmap) {
Ok(data) => Some(format!(
"data:image/bmp;base64,{}",
general_purpose::STANDARD.encode(data)
)),
Err(_) => None,
}
}
/**
*
*/
pub fn empty_recycle_bin(window: i32) {
// HWND
let hwnd = HWND(window as isize);
// 清空回收站
unsafe {
let _ = SHEmptyRecycleBinW(hwnd, None, SHERB_NOSOUND);
};
}

282
src/App.vue Normal file
View File

@ -0,0 +1,282 @@
<template>
<NConfigProvider :theme-overrides="themeOverrides">
<NMessageProvider><router-view></router-view></NMessageProvider>
</NConfigProvider>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from "vue";
import {
NConfigProvider,
GlobalThemeOverrides,
NMessageProvider,
} from "naive-ui";
import { hexToRGBA } from "./utils/style";
import { getSetting } from "../commons/utils/setting";
import { useMainStore } from "./store";
import { getLanguage } from "../commons/data/languages";
import { getShortcutKey } from "./utils/common";
// pinia
const store = useMainStore();
//
(async () => {
let setting = window.setting.select();
if (setting) {
store.setting = getSetting(setting);
} else {
let setting = getSetting(null);
window.setting.add(setting);
store.setting = setting;
}
store.language = getLanguage(store.setting.general.language);
})();
//
let themeOverrides = ref<GlobalThemeOverrides | null>(null);
//
function setTheme() {
themeOverrides.value = {
common: {
borderColor: store.setting.appearance.theme.borderColor,
cubicBezierEaseInOut: "none",
cubicBezierEaseOut: "none",
cubicBezierEaseIn: "none",
},
Input: {
color: store.setting.appearance.theme.mainBackgroundColor,
colorFocus: store.setting.appearance.theme.mainBackgroundColor,
textColor: store.setting.appearance.theme.mainFontColor,
caretColor: store.setting.appearance.theme.mainFontColor,
borderHover: "1px solid " + store.setting.appearance.theme.borderColor,
borderFocus: "1px solid " + store.setting.appearance.theme.borderColor,
boxShadowFocus: "none",
placeholderColor: hexToRGBA(
store.setting.appearance.theme.mainFontColor,
0.3
),
},
Button: {
colorPrimary: store.setting.appearance.theme.secondBackgroundColor,
colorFocusPrimary: store.setting.appearance.theme.secondBackgroundColor,
colorHoverPrimary: hexToRGBA(
store.setting.appearance.theme.secondBackgroundColor,
0.7
),
colorDisabledPrimary:
store.setting.appearance.theme.secondBackgroundColor,
borderDisabledPrimary: "none",
borderPrimary: "none",
borderHoverPrimary: "none",
borderFocusPrimary: "none",
borderPressedPrimary: "none",
colorPressedPrimary: hexToRGBA(
store.setting.appearance.theme.secondBackgroundColor,
0.7
),
textColorPrimary: store.setting.appearance.theme.secondFontColor,
textColorHoverPrimary: store.setting.appearance.theme.secondFontColor,
textColorFocusPrimary: store.setting.appearance.theme.secondFontColor,
textColorPressedPrimary: store.setting.appearance.theme.secondFontColor,
textColor: store.setting.appearance.theme.mainFontColor,
textColorHover: "none",
textColorFocus: "none",
borderHover: "none",
borderPressed: "none",
textColorPressed: "none",
rippleDuration: "none",
},
InternalSelection: {
boxShadowFocus: "none",
boxShadowActive: "none",
},
Message: {
padding: "6px",
},
Checkbox: {
color: store.setting.appearance.theme.mainBackgroundColor,
textColor: store.setting.appearance.theme.mainFontColor,
borderChecked: "1px solid " + store.setting.appearance.theme.borderColor,
colorChecked: store.setting.appearance.theme.secondBackgroundColor,
},
Form: {
labelTextColor: store.setting.appearance.theme.mainFontColor,
},
Dropdown: {
color: store.setting.appearance.theme.mainBackgroundColor,
optionColorHover: store.setting.appearance.theme.secondBackgroundColor,
optionTextColor: store.setting.appearance.theme.mainFontColor,
optionTextColorHover: store.setting.appearance.theme.secondFontColor,
dividerColor: store.setting.appearance.theme.borderColor,
},
Select: {
peers: {
InternalSelection: {
textColor: store.setting.appearance.theme.mainFontColor,
color: store.setting.appearance.theme.mainBackgroundColor,
colorActive: store.setting.appearance.theme.mainBackgroundColor,
borderHover:
"1px solid " + store.setting.appearance.theme.borderColor,
borderFocus:
"1px solid " + store.setting.appearance.theme.borderColor,
borderActive:
"1px solid " + store.setting.appearance.theme.borderColor,
},
InternalSelectMenu: {
color: store.setting.appearance.theme.mainBackgroundColor,
optionColorPending: hexToRGBA(
store.setting.appearance.theme.secondBackgroundColor,
0.3
),
optionColorActivePending: hexToRGBA(
store.setting.appearance.theme.secondBackgroundColor,
0.3
),
actionTextColor: store.setting.appearance.theme.mainFontColor,
optionTextColorActive: store.setting.appearance.theme.mainFontColor,
optionTextColor: store.setting.appearance.theme.mainFontColor,
optionTextColorPressed: store.setting.appearance.theme.mainFontColor,
optionTextColorDisabled: store.setting.appearance.theme.mainFontColor,
optionCheckColor: store.setting.appearance.theme.mainFontColor,
},
},
},
Slider: {
fillColor: store.setting.appearance.theme.secondBackgroundColor,
fillColorHover: store.setting.appearance.theme.secondBackgroundColor,
handleColor: store.setting.appearance.theme.secondBackgroundColor,
railColor: store.setting.appearance.theme.secondBackgroundColor,
railColorHover: store.setting.appearance.theme.secondBackgroundColor,
},
ColorPicker: {
color: store.setting.appearance.theme.mainBackgroundColor,
textColor: store.setting.appearance.theme.mainFontColor,
},
Spin: {
color: store.setting.appearance.theme.secondBackgroundColor,
},
Popselect: {
peers: {
Popover: {
color: store.setting.appearance.theme.mainBackgroundColor,
},
},
},
};
}
//
setTheme();
//
watch(
() => store.setting.appearance.theme,
async () => {
setTheme();
createStyle();
}
);
function createStyle() {
// style
let oldRangeStyleElement = document.getElementById("range-style");
// style
if (oldRangeStyleElement && oldRangeStyleElement.parentNode) {
oldRangeStyleElement.parentNode.removeChild(oldRangeStyleElement);
}
//
let rangeStyleElement = document.createElement("style");
rangeStyleElement.setAttribute("id", "range-style");
rangeStyleElement.type = "text/css";
//
rangeStyleElement.innerHTML =
".range::-webkit-slider-runnable-track {" +
"background-color: " +
store.setting.appearance.theme.secondBackgroundColor +
"!important;" +
"border-radius: 0.5rem!important;" +
"height: 4px!important;" +
"}" +
".range::-webkit-slider-thumb {" +
"-webkit-appearance: none!important; " +
"appearance: none!important;" +
"margin-top: -8px!important;" +
"border-radius: 50%!important;" +
"background-color: " +
store.setting.appearance.theme.secondBackgroundColor +
"!important;" +
"border: 1px solid " +
store.setting.appearance.theme.secondBackgroundColor +
"!important;" +
"width: 20px!important;" +
"height: 20px!important;" +
"}";
// head
document.head.appendChild(rangeStyleElement);
// style
let oldScrollStyleElement = document.getElementById("scroll-style");
// style
if (oldScrollStyleElement && oldScrollStyleElement.parentNode) {
oldScrollStyleElement.parentNode.removeChild(oldScrollStyleElement);
}
//
let scrollStyleElement = document.createElement("style");
scrollStyleElement.setAttribute("id", "scroll-style");
scrollStyleElement.type = "text/css";
//
scrollStyleElement.innerHTML =
".simplebar-scrollbar::before {" +
" background-color: " +
store.setting.appearance.theme.secondBackgroundColor +
"!important;" +
" right: 0!important;" +
"}" +
"textarea::-webkit-scrollbar-thumb {" +
" background-color: " +
store.setting.appearance.theme.secondBackgroundColor +
"!important;" +
"border-radius: 7px!important;" +
"}";
// head
document.head.appendChild(scrollStyleElement);
}
//
function keydown(e: any) {
let prod = import.meta.env.PROD;
if (prod) {
//
let shortcutKey = getShortcutKey(e, null, false);
if (shortcutKey) {
//
if (
shortcutKey.toLowerCase() === "ctrl + r" ||
shortcutKey.toLowerCase() === "ctrl + shift + r" ||
shortcutKey.toLowerCase() === "f5"
) {
e.preventDefault();
}
//
if (shortcutKey.toLowerCase() === "ctrl + w") {
e.preventDefault();
}
}
}
}
//
let onUpdateSettingUnListen: Function | null = null;
// mounted
onMounted(() => {
//
createStyle();
//
window.addEventListener("keydown", keydown, true);
//
onUpdateSettingUnListen = window.setting.onUpdate((data) => {
store.setting = data;
});
});
// unmounted
onUnmounted(() => {
//
window.removeEventListener("keydown", keydown, true);
//
if (onUpdateSettingUnListen) {
onUpdateSettingUnListen();
}
});
</script>

View File

@ -0,0 +1,91 @@
<template>
<template v-if="item && item.data">
<!-- 普通图标 -->
<template v-if="item.data.icon">
<!-- 使用背景 -->
<template v-if="item.data.iconBackgroundColor">
<div
class="flex items-center justify-center"
style="background-color: rgb(0, 120, 215)"
:style="{
width: iconSize + 'px',
height: iconSize + 'px',
minWidth: iconSize + 'px',
minHeight: iconSize + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
>
<img
:src="item.data.icon"
:style="{
width: 20 - 8 + 'px',
height: 20 - 8 + 'px',
}"
:draggable="false"
/>
</div>
</template>
<!-- 不使用背景 -->
<img
v-else
:src="item.data.icon"
class="block"
:style="{
width: iconSize + 'px',
height: iconSize + 'px',
minWidth: iconSize + 'px',
minHeight: iconSize + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
:draggable="false"
/>
</template>
<!-- SVG代码图标 -->
<div
v-if="item.data.htmlIcon"
:style="{
width: iconSize + 'px',
height: iconSize + 'px',
minWidth: iconSize + 'px',
minHeight: iconSize + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
v-html="DOMPurify.sanitize(item.data.htmlIcon)"
></div>
</template>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import DOMPurify from "dompurify";
import { Item } from "../../types/item";
import { useMainStore } from "../store";
// pinia
const store = useMainStore();
// props
const props = defineProps<{
item: Item | null;
iconSize: number;
}>();
//
let item = ref<Item | null>(props.item);
//
watch(
() => props.item,
(newValue: Item | null) => {
item.value = newValue;
}
);
</script>
../../types/item

20
src/components/Desc.vue Normal file
View File

@ -0,0 +1,20 @@
<template>
<span
v-if="content"
class="block text-xs"
:style="{
color: hexToRGBA(store.setting.appearance.theme.mainFontColor, 0.7),
}"
>{{ content }}</span
>
</template>
<script setup lang="ts">
import { useMainStore } from "../store";
import { hexToRGBA } from "../utils/style";
// pinia
const store = useMainStore();
// props
const props = defineProps<{
content: string | null;
}>();
</script>

172
src/components/ItemIcon.vue Normal file
View File

@ -0,0 +1,172 @@
<template>
<template v-if="item && item.data">
<!-- 无效项目 -->
<template
v-if="
store.setting.item.checkInvalidItem &&
store.invalidItemIdList.includes(item.id)
"
>
<svg
class="block"
:class="[
`${
getLayout(classificationId) === 'tile' ||
store.setting.item.hideItemName
? 'mx-auto'
: 'ml-2'
} `,
]"
viewBox="0 0 1024 1024"
:style="{
width: getIconSize(classificationId) + 'px',
height: getIconSize(classificationId) + 'px',
minWidth: getIconSize(classificationId) + 'px',
minHeight: getIconSize(classificationId) + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
>
<path
d="M969.182003 701.303054C942.98583 761.818425 907.560805 814.394635 862.909139 859.030351 818.273423 903.682017 765.86402 939.121773 705.727336 965.303215 645.576031 991.515341 581.182839 1004.606061 512.545547 1004.606061 443.925315 1004.606061 379.349482 991.515341 318.818158 965.303215 258.302785 939.121773 205.726577 903.682017 161.090861 859.030351 116.439196 814.393527 80.999438 761.818425 54.817996 701.303054 28.605873 640.772838 15.515152 576.197117 15.515152 507.575665 15.515152 438.938373 28.605873 374.545181 54.817996 314.393878 80.999438 254.257304 116.439196 201.8479 161.090861 157.212073 205.726577 112.560409 258.302785 77.135381 318.818158 50.939208 379.349482 24.743034 443.925203 11.636364 512.545547 11.636364 581.182839 11.636364 645.576031 24.742924 705.727336 50.939208 765.8628 77.1366 818.273311 112.560409 862.909139 157.212073 907.560805 201.848897 942.984611 254.258412 969.182003 314.393878 995.378179 374.545181 1008.484848 438.938373 1008.484848 507.575665 1008.484848 576.197117 995.379287 640.772838 969.182003 701.303054L969.182003 701.303054Z"
fill="#d81e06"
data-spm-anchor-id="a313x.7781069.0.i1"
class="selected"
></path>
<path
d="M512 709.220647C472.332325 709.220647 440.203301 741.349668 440.203301 781.017346 440.203301 820.68502 472.332325 852.814044 512 852.814044 551.667675 852.814044 583.796699 820.68502 583.796699 781.017346 583.796699 741.349668 551.667675 709.220647 512 709.220647L512 709.220647Z"
fill="#FFFFFF"
></path>
<path
d="M512 639.709091C492.184111 639.709091 476.101651 606.196596 476.101651 564.820837L440.203301 227.823683C440.203301 186.447921 472.332325 152.935427 512 152.935427 551.667675 152.935427 583.796699 186.447921 583.796699 227.823683L547.898349 564.820837C547.898349 606.196596 531.815889 639.709091 512 639.709091L512 639.709091Z"
fill="#FFFFFF"
></path>
</svg>
</template>
<!-- 普通图标 -->
<template v-else-if="item.data.icon && item.data.icon.trim() !== ''">
<!-- 使用背景 -->
<template v-if="item.data.iconBackgroundColor">
<div
class="flex items-center justify-center"
:class="[
`${
getLayout(classificationId) === 'tile' ||
store.setting.item.hideItemName
? 'mx-auto'
: 'ml-2'
} `,
]"
style="background-color: rgb(0, 120, 215)"
:style="{
width: getIconSize(classificationId) + 'px',
height: getIconSize(classificationId) + 'px',
minWidth: getIconSize(classificationId) + 'px',
minHeight: getIconSize(classificationId) + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
>
<img
:src="item.data.icon"
:style="{
width: getIconSize(classificationId) - 8 + 'px',
height: getIconSize(classificationId) - 8 + 'px',
}"
:draggable="false"
/>
</div>
</template>
<!-- 不使用背景 -->
<img
v-else
:src="item.data.icon"
class="block"
:class="[
`${
getLayout(classificationId) === 'tile' ||
store.setting.item.hideItemName
? 'mx-auto'
: 'ml-2'
} `,
]"
:style="{
width: getIconSize(classificationId) + 'px',
height: getIconSize(classificationId) + 'px',
minWidth: getIconSize(classificationId) + 'px',
minHeight: getIconSize(classificationId) + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
:draggable="false"
/>
</template>
<!-- SVG代码图标 -->
<div
v-else-if="item.data.htmlIcon && item.data.htmlIcon.trim() !== ''"
:class="[
`${
getLayout(classificationId) === 'tile' ||
store.setting.item.hideItemName
? 'mx-auto'
: 'ml-2'
} `,
]"
:style="{
width: getIconSize(classificationId) + 'px',
height: getIconSize(classificationId) + 'px',
minWidth: getIconSize(classificationId) + 'px',
minHeight: getIconSize(classificationId) + 'px',
filter: store.setting.appearance.fontShadow
? 'drop-shadow(1px 1px 1px ' +
store.setting.appearance.fontShadowColor +
')'
: undefined,
}"
v-html="DOMPurify.sanitize(item.data.htmlIcon)"
></div>
</template>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import DOMPurify from "dompurify";
import { Item } from "../../types/item";
import { getLayout, getIconSize } from "../pages/item/js/index";
import { useMainStore } from "../store";
// pinia
const store = useMainStore();
// props
const props = defineProps<{
classificationId: number | null;
item: Item | null;
}>();
// ID
let classificationId = ref(props.classificationId);
//
watch(
() => props.classificationId,
(newValue: number | null) => {
//
classificationId.value = newValue;
}
);
//
let item = ref<Item | null>(props.item);
//
watch(
() => props.item,
(newValue: Item | null) => {
item.value = newValue;
}
);
</script>
../../types/item

185
src/index.d.ts vendored Normal file
View File

@ -0,0 +1,185 @@
import { Classification } from "../types/classification";
import { Item } from "../types/item";
type Callback = (param: any) => void;
declare global {
interface Window {
api: {
emit: (windowName: string, listener: string, paylod: any) => void;
showErrorMessageBox: (windowName: string, message: string) => void;
showInfoMessageBox: (windowName: string, message: string) => void;
showConfirmBox: (windowName: string, message: string) => boolean;
selectFile: (
windowName: string,
target: boolean,
defaultPath: string | null
) => string | null;
selectDirectory: (
windowName: string,
defaultPath: string | null
) => string | null;
getFileIcon: (windowName: string, path: string) => void;
onGetFileIcon: (callback: Callback) => Function;
downloadImage: (windowName: string, url: string) => void;
onDownloadImage: (callback: Callback) => Function;
getURLInfo: (windowName: string, url: string) => void;
onGetURLInfo: (callback: Callback) => Function;
convertPath: (path: string) => string;
pathExist: (path: string) => boolean;
isFile: (path: string) => boolean;
openURL: (url: string) => void;
getVersion: () => string;
exit: () => void;
};
main: {
showWindow: (blurHide: boolean) => void;
hideWindow: () => void;
initData: () => void;
onShowWindowBefore: (callback: Callback) => Function;
};
classification: {
list: () => Array<Classification>;
selectById: (id: number) => Classification | null;
add: (
parentId: number | null,
name: string,
shortcutKey: string | null,
globalShortcutKey: boolean
) => Classification | null;
update: (classifictaion: Classification) => boolean;
updateOrder: (
fromId: number,
toId: number | null,
parentId: number | null
) => boolean;
updateIcon: (id: number, icon: string | null) => boolean;
showAddEditWindow: () => void;
closeAddEditWindow: () => void;
showSetIconWindow: () => void;
closeSetIconWindow: () => void;
showRightMenu: (
classification: Classification | null,
lockClassification: boolean
) => void;
onAdd: (callback: Callback) => Function;
onUpdate: (callback: Callback) => Function;
onDelete: (callback: Callback) => Function;
onLock: (callback: Callback) => Function;
onUpdateIcon: (callback: Callback) => Function;
showAssociateFolderWindow: () => void;
closeAssociateFolderWindow: () => void;
setAssociateFolder: (
id: number,
dir: string | null,
hiddenItems: string | null
) => Classification;
onUpdateAssociateFolder: (callback: Callback) => Function;
hasChildClassification: (id: number) => boolean;
onCollapseSubClassification: (callback: Callback) => Function;
onUpdateItemLayout: (callback: Callback) => Function;
onUpdateItemSort: (callback: Callback) => Function;
onUpdateItemColumnNumber: (callback: Callback) => Function;
onUpdateItemIconSize: (callback: Callback) => Function;
addClassificationByDirectory: (pathList: Array<string>) => void;
onAddClassificationByDirectory: (callback: Callback) => Function;
onUpdateItemShowOnly: (callback: Callback) => Function;
onUpdateFixed: (callback: Callback) => Function;
onUpdateItemOpenNumberSortToDefualt: (callback: Callback) => Function;
showAggregateWindow: () => void;
closeAggregateWindow: () => void;
updateAggregate: (id: number, sort: string, itemCount: number) => boolean;
onUpdateAggregate: (callback: Callback) => Function;
onUpdateExcludeSearch: (callback: Callback) => Function;
};
item: {
showAddEditWindow: () => void;
closeAddEditWindow: () => void;
list: () => Array<Item>;
simpleList: () => Array<Item>;
selectById: (id: number) => Item | null;
add: (item: Item) => Item | null;
update: (item: Item) => boolean;
updateOrder: (
fromIdList: Array<number>,
toClassificationId: number,
newIndex: number | null
) => boolean;
showRightMenu: (params: any) => void;
createNetworkIconWindow: () => void;
showNetworkIconWindow: () => void;
closeNetworkIconWindow: () => void;
onNetworkIcon: (callback: Callback) => Function;
createSVGIconWindow: () => void;
showSVGIconWindow: () => void;
closeSVGIconWindow: () => void;
onSVGIcon: (callback: Callback) => Function;
getSystemItemList: () => void;
onGetSystemItemList: (callback: Callback) => Function;
getStartMenuItemList: () => void;
onGetStartMenuItemList: (callback: Callback) => Function;
getAppxItemList: () => void;
onGetAppxItemList: (callback: Callback) => Function;
onAdd: (callback: Callback) => Function;
onUpdate: (callback: Callback) => Function;
onDelete: (callback: Callback) => Function;
onLock: (callback: Callback) => Function;
drop: (classificationId: number, pathList: Array<string>) => void;
onBatchOperation: (callback: Callback) => Function;
run: (type: string, operation: string, item: Item) => void;
onConvertPath: (callback: Callback) => Function;
onRefreshIcon: (callback: Callback) => Function;
onMove: (callback: Callback) => Function;
onBatchOperationSelectAll: (callback: Callback) => Function;
onRightMenuClose: (callback: Callback) => Function;
onExplorerMenu: (callback: Callback) => Function;
dragOut: (item: Item) => void;
onCancelDragOut: (callback: Callback) => Function;
onUpdateOpenInfo: (callback: Callback) => Function;
onCheckInvalid: (callback: Callback) => Function;
updateOpenInfo: (type: string, id: number) => void;
};
setting: {
createWindow: () => void;
showWindow: () => void;
closeWindow: () => void;
select: () => Setting | null;
add: (setting: Setting) => boolean;
update: (setting: Setting) => boolean;
onUpdate: (callback: Callback) => Function;
setStartup: (value: boolean) => void;
setTray: (show: boolean) => void;
setShortcutKey: (setting: Setting) => void;
setAlwaysTop: (value: boolean) => void;
setLockSize: (value: boolean) => void;
setFixedPosition: (fixedPosition: boolean, alwaysCenter: boolean) => void;
setAlwaysCenter: (fixedPosition: boolean, alwaysCenter: boolean) => void;
setEdgeAdsorb: (value: boolean) => void;
uploadBackgrounImage: () => any;
onSetBacngroundImage: (callback: Callback) => Function;
getBackgroundImage: (name: string, windowName: string) => void;
setCheckInvalidItem: (value: boolean) => void;
setOpenNumber: (value: boolean) => void;
};
quickSearch: {
initFinished: () => void;
showWindow: () => void;
hideWindow: () => void;
onShowWindowBefore: (callback: Callback) => Function;
onClearData: (callback: Callback) => Function;
setWindowHeight: (height: number) => void;
};
about: {
createWindow: () => void;
showWindow: () => void;
closeWindow: () => void;
};
data: {
createBackupRestoreDataWindow: () => void;
showBackupRestoreDataWindow: () => void;
closeBackupRestoreDataWindow: () => void;
backupData: () => void;
restoreData: () => void;
};
}
}

9
src/main.ts Normal file
View File

@ -0,0 +1,9 @@
import { createApp } from "vue";
import "./styles/style.css";
import "./styles/tailwind.css";
import router from "./router/index";
import App from "./App.vue";
import { createPinia } from "pinia";
const pinia = createPinia();
createApp(App).use(router).use(pinia).mount("#app");

View File

@ -1,589 +0,0 @@
#include <napi.h>
#include <windows.h>
#include <atlimage.h>
#include <iostream>
#include <shobjidl.h>
#include <shlguid.h>
#include <string>
#include <atomic>
#include <shlobj.h>
#include <imm.h>
#pragma comment(lib, "imm32.lib")
std::string UTF8ToGBK(const char *source)
{
int length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0);
wchar_t *wcGBK = new wchar_t[length + 1];
memset(wcGBK, 0, length * 2 + 2);
MultiByteToWideChar(CP_UTF8, 0, source, -1, wcGBK, length);
length = WideCharToMultiByte(CP_ACP, 0, wcGBK, -1, NULL, 0, NULL, NULL);
char *cGBK = new char[length + 1];
memset(cGBK, 0, length + 1);
WideCharToMultiByte(CP_ACP, 0, wcGBK, -1, cGBK, length, NULL, NULL);
std::string strTemp(cGBK);
if (wcGBK)
delete[] wcGBK;
if (cGBK)
delete[] cGBK;
return strTemp;
}
std::string GBKToUTF8(const char *source)
{
int length = MultiByteToWideChar(CP_ACP, 0, source, -1, NULL, 0);
wchar_t *wStr = new wchar_t[length + 1];
memset(wStr, 0, length + 1);
MultiByteToWideChar(CP_ACP, 0, source, -1, wStr, length);
length = WideCharToMultiByte(CP_UTF8, 0, wStr, -1, NULL, 0, NULL, NULL);
char *str = new char[length + 1];
memset(str, 0, length + 1);
WideCharToMultiByte(CP_UTF8, 0, wStr, -1, str, length, NULL, NULL);
std::string strTemp = str;
if (wStr)
delete[] wStr;
if (str)
delete[] str;
return strTemp;
}
LPCWSTR StringToLPCWSTR(std::string source)
{
size_t size = source.length();
int wLen = ::MultiByteToWideChar(CP_UTF8,
0,
source.c_str(),
-1,
NULL,
0);
wchar_t *buffer = new wchar_t[wLen + 1];
memset(buffer, 0, (wLen + 1) * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, 0, source.c_str(), size, (LPWSTR)buffer, wLen);
return buffer;
}
Napi::Number SaveIcon(std::string source, std::string target, int size, Napi::Env env)
{
CoInitialize(NULL);
IShellItemImageFactory *itemImageFactory;
HBITMAP bitmap;
SIZE s = {size, size};
if (SUCCEEDED(SHCreateItemFromParsingName(StringToLPCWSTR(source), NULL, IID_PPV_ARGS(&itemImageFactory))))
{
itemImageFactory->GetImage(s, SIIGBF_ICONONLY, &bitmap);
itemImageFactory->Release();
}
CoUninitialize();
if (NULL == &bitmap)
{
return Napi::Number::New(env, 0);
}
else
{
CImage image;
image.Attach(bitmap);
image.SetHasAlphaChannel(1);
image.Save(target.c_str());
return Napi::Number::New(env, 1);
}
}
Napi::Number GetFileIcon(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
std::string source = info[0].ToString().Utf8Value();
std::string target = info[1].ToString().Utf8Value();
int size = info[2].As<Napi::Number>().Int32Value();
return SaveIcon(UTF8ToGBK(source.c_str()), UTF8ToGBK(target.c_str()), size, env);
}
Napi::Object GetShortcutFile(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
std::string source = info[0].ToString().Utf8Value();
source = UTF8ToGBK(source.c_str());
Napi::Object shortcutFileInfo;
CoInitialize(NULL);
CHAR target[MAX_PATH];
CHAR arguments[MAX_PATH];
WIN32_FIND_DATA fd;
IShellLink *shellLink;
HRESULT result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&shellLink);
if (SUCCEEDED(result))
{
IPersistFile *persistFile;
result = shellLink->QueryInterface(IID_IPersistFile, (void **)&persistFile);
if (SUCCEEDED(result))
{
result = persistFile->Load(StringToLPCWSTR(source), STGM_READ);
if (SUCCEEDED(result))
{
shellLink->GetPath(target, MAX_PATH, &fd, SLGP_UNCPRIORITY);
shellLink->GetArguments(arguments, MAX_PATH);
shortcutFileInfo = Napi::Object::New(env);
shortcutFileInfo.Set(Napi::String::New(env, "target"), Napi::String::New(env, GBKToUTF8(target)));
shortcutFileInfo.Set(Napi::String::New(env, "arguments"), Napi::String::New(env, GBKToUTF8(arguments)));
}
}
if (NULL != persistFile)
{
persistFile->Release();
}
}
if (NULL != shellLink)
{
shellLink->Release();
}
CoUninitialize();
return shortcutFileInfo;
}
Napi::Number RunItem(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
std::string type = info[0].ToString().Utf8Value();
std::string source = info[1].ToString().Utf8Value();
std::string parameters = info[2].ToString().Utf8Value();
LPCWSTR ldir;
if (info[3] != NULL)
{
std::string dir = info[3].ToString().Utf8Value();
ldir = StringToLPCWSTR(UTF8ToGBK(dir.c_str()));
}
return Napi::Number::New(env, (unsigned long)ShellExecuteW(NULL, StringToLPCWSTR(type), StringToLPCWSTR(UTF8ToGBK(source.c_str())), StringToLPCWSTR(UTF8ToGBK(parameters.c_str())), ldir, SW_SHOWDEFAULT));
}
Napi::ThreadSafeFunction _tsfn;
HANDLE _hThread;
std::atomic_bool captureMouseMove = false;
// PostThreadMessage races with the actual thread; we'll get a thread ID
// and won't be able to post to it because it's "invalid" during the early
// lifecycle of the thread. To ensure that immediate pauses don't get dropped,
// we'll use this flag instead of distinct message IDs.
std::atomic_bool installEventHook = false;
DWORD dwThreadID = 0;
struct MouseEventContext
{
public:
int nCode;
WPARAM wParam;
LONG ptX;
LONG ptY;
DWORD mouseData;
};
void onMainThread(Napi::Env env, Napi::Function function, MouseEventContext *pMouseEvent)
{
auto nCode = pMouseEvent->nCode;
auto wParam = pMouseEvent->wParam;
auto ptX = pMouseEvent->ptX;
auto ptY = pMouseEvent->ptY;
auto nMouseData = pMouseEvent->mouseData;
delete pMouseEvent;
if (nCode >= 0)
{
auto name = "";
auto button = -1;
// Isolate mouse movement, as it's more CPU intensive
if (wParam == WM_MOUSEMOVE)
{
// Is mouse movement
if (captureMouseMove.load())
{
name = "mousemove";
}
}
else
{
// Is not mouse movement
// Determine event type
if (wParam == WM_LBUTTONUP || wParam == WM_RBUTTONUP || wParam == WM_MBUTTONUP)
{
name = "mouseup";
}
else if (wParam == WM_LBUTTONDOWN || wParam == WM_RBUTTONDOWN || wParam == WM_MBUTTONDOWN)
{
name = "mousedown";
}
else if (wParam == WM_MOUSEWHEEL || wParam == WM_MOUSEHWHEEL)
{
name = "mousewheel";
}
// Determine button
if (wParam == WM_LBUTTONUP || wParam == WM_LBUTTONDOWN)
{
button = 1;
}
else if (wParam == WM_RBUTTONUP || wParam == WM_RBUTTONDOWN)
{
button = 2;
}
else if (wParam == WM_MBUTTONUP || wParam == WM_MBUTTONDOWN)
{
button = 3;
}
else if (wParam == WM_MOUSEWHEEL)
{
button = 0;
}
else if (wParam == WM_MOUSEHWHEEL)
{
button = 1;
}
}
// Only proceed if an event was identified
if (name != "")
{
Napi::HandleScope scope(env);
auto x = Napi::Number::New(env, ptX);
auto y = Napi::Number::New(env, ptY);
auto mouseData = Napi::Number::New(env, nMouseData);
// Yell back to NodeJS
function.Call(env.Global(),
{Napi::String::New(env, name), x, y,
Napi::Number::New(env, button), mouseData});
}
}
}
LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
// If not WM_MOUSEMOVE or WM_MOUSEMOVE has been requested, process event
if (!(wParam == WM_MOUSEMOVE && !captureMouseMove.load()))
{
// Prepare data to be processed
MSLLHOOKSTRUCT *data = (MSLLHOOKSTRUCT *)lParam;
auto pMouseEvent = new MouseEventContext();
pMouseEvent->nCode = nCode;
pMouseEvent->wParam = wParam;
pMouseEvent->ptX = data->pt.x;
pMouseEvent->ptY = data->pt.y;
pMouseEvent->mouseData = data->mouseData;
// Process event on non-blocking thread
_tsfn.NonBlockingCall(pMouseEvent, onMainThread);
}
// Let Windows continue with this event as normal
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
DWORD WINAPI MouseHookThread(LPVOID lpParam)
{
MSG msg;
HHOOK hook = installEventHook.load() ? SetWindowsHookEx(WH_MOUSE_LL, HookCallback, NULL, 0) : NULL;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
if (msg.message != WM_USER)
continue;
if (!installEventHook.load() && hook != NULL)
{
if (!UnhookWindowsHookEx(hook))
break;
hook = NULL;
}
else if (installEventHook.load() && hook == NULL)
{
hook = SetWindowsHookEx(WH_MOUSE_LL, HookCallback, NULL, 0);
if (hook == NULL)
break;
}
}
_tsfn.Release();
return GetLastError();
}
Napi::Boolean createMouseHook(const Napi::CallbackInfo &info)
{
_hThread = CreateThread(NULL, 0, MouseHookThread, NULL, CREATE_SUSPENDED, &dwThreadID);
_tsfn = Napi::ThreadSafeFunction::New(
info.Env(),
info[0].As<Napi::Function>(),
"WH_MOUSE_LL Hook Thread",
512,
1,
[](Napi::Env)
{ CloseHandle(_hThread); });
ResumeThread(_hThread);
return Napi::Boolean::New(info.Env(), true);
}
void enableMouseMove(const Napi::CallbackInfo &info)
{
captureMouseMove = true;
}
void disableMouseMove(const Napi::CallbackInfo &info)
{
captureMouseMove = false;
}
Napi::Boolean pauseMouseEvents(const Napi::CallbackInfo &info)
{
BOOL bDidPost = FALSE;
if (dwThreadID != 0)
{
installEventHook = false;
bDidPost = PostThreadMessageW(dwThreadID, WM_USER, NULL, NULL);
}
return Napi::Boolean::New(info.Env(), bDidPost);
}
Napi::Boolean resumeMouseEvents(const Napi::CallbackInfo &info)
{
BOOL bDidPost = FALSE;
if (dwThreadID != 0)
{
installEventHook = true;
bDidPost = PostThreadMessageW(dwThreadID, WM_USER, NULL, NULL);
}
return Napi::Boolean::New(info.Env(), bDidPost);
}
Napi::Boolean IsFullscreenSize(const Napi::CallbackInfo &info)
{
// 获取当前活动窗口的句柄
HWND foregroundWindow = GetForegroundWindow();
// 获取活动窗口的位置信息
RECT windowRect;
GetWindowRect(foregroundWindow, &windowRect);
// 获取包含活动窗口的显示器句柄
HMONITOR monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTONEAREST);
// 获取显示器信息
MONITORINFO monitorInfo = { sizeof(MONITORINFO) };
GetMonitorInfo(monitor, &monitorInfo);
// 获取屏幕的尺寸
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
// 比较窗口位置和显示器尺寸来判断是否处于全屏模式
if (windowRect.left <= 0 && windowRect.top <= 0 &&
windowRect.right >= screenWidth && windowRect.bottom >= screenHeight &&
monitorInfo.rcMonitor.left == 0 && monitorInfo.rcMonitor.top == 0 &&
monitorInfo.rcMonitor.right == screenWidth && monitorInfo.rcMonitor.bottom == screenHeight)
{
// 获取窗口类名
char className[256];
GetClassName(foregroundWindow, className, sizeof(className));
std::string classNameStr(className);
if (classNameStr != "WorkerW")
{
return Napi::Boolean::New(info.Env(), true);
}
}
return Napi::Boolean::New(info.Env(), false);
}
Napi::Boolean IsFullscreen(const Napi::CallbackInfo &info)
{
QUERY_USER_NOTIFICATION_STATE state;
HRESULT hr = SHQueryUserNotificationState(&state);
if (hr == S_OK)
{
switch (state)
{
case QUNS_NOT_PRESENT:
// 非全屏(机器锁定/屏幕保护程序/用户切换)
return Napi::Boolean::New(info.Env(), false);
case QUNS_BUSY:
// 全屏F11 全屏,我试过的所有视频游戏都使用它)
return IsFullscreenSize(info);
case QUNS_RUNNING_D3D_FULL_SCREEN:
// 全屏Direct3D 应用程序以独占模式运行,即全屏)
return Napi::Boolean::New(info.Env(), true);
case QUNS_PRESENTATION_MODE:
// 全屏(一种用于显示全屏演示文稿的特殊模式)
return Napi::Boolean::New(info.Env(), true);
case QUNS_ACCEPTS_NOTIFICATIONS:
// 不是全屏
return Napi::Boolean::New(info.Env(), false);
case QUNS_QUIET_TIME:
// 不是全屏
return Napi::Boolean::New(info.Env(), false);
case QUNS_APP:
// 不是全屏
return Napi::Boolean::New(info.Env(), false);
}
return Napi::Boolean::New(info.Env(), false);
}
else
{
return Napi::Boolean::New(info.Env(), false);
}
}
Napi::Boolean ContextMenu(const Napi::CallbackInfo &info)
{
// CoInitialize
CoInitialize(NULL);
// 获取文件路径
std::string filePath = info[1].ToString().Utf8Value();
// 获取文件的 IShellItem 接口
IShellItem *pItem;
HRESULT hr = SHCreateItemFromParsingName(StringToLPCWSTR(UTF8ToGBK(filePath.c_str())), NULL, IID_PPV_ARGS(&pItem));
if (FAILED(hr))
{
return Napi::Boolean::New(info.Env(), false);
}
// 获取文件的 IContextMenu 接口
IContextMenu *pContextMenu;
hr = pItem->BindToHandler(NULL, BHID_SFUIObject, IID_PPV_ARGS(&pContextMenu));
if (FAILED(hr))
{
pItem->Release();
return Napi::Boolean::New(info.Env(), false);
}
// 创建菜单
HMENU hMenu = CreatePopupMenu();
if (hMenu == NULL)
{
pContextMenu->Release();
pItem->Release();
return Napi::Boolean::New(info.Env(), false);
}
hr = pContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL);
if (FAILED(hr))
{
DestroyMenu(hMenu);
pContextMenu->Release();
pItem->Release();
return Napi::Boolean::New(info.Env(), false);
}
// 获取当前窗口句柄
Napi::Buffer<void *> buffer = info[0].As<Napi::Buffer<void *>>();
HWND hWnd = static_cast<HWND>(*reinterpret_cast<void **>(buffer.Data()));
if (!IsWindow(hWnd))
{
return Napi::Boolean::New(info.Env(), false);
}
// 弹出菜单
int command = TrackPopupMenuEx(hMenu, TPM_RETURNCMD | TPM_NONOTIFY, info[2].As<Napi::Number>(), info[3].As<Napi::Number>(), hWnd, NULL);
if (command > 0)
{
CMINVOKECOMMANDINFOEX info = {0};
info.cbSize = sizeof(info);
info.hwnd = hWnd;
info.lpVerb = MAKEINTRESOURCEA(command - 1);
info.nShow = SW_NORMAL;
pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
}
// 释放资源
DestroyMenu(hMenu);
pContextMenu->Release();
pItem->Release();
// CoUninitialize
CoUninitialize();
return Napi::Boolean::New(info.Env(), true);
}
Napi::Boolean SwitchEnglish(const Napi::CallbackInfo &info)
{
// 获取当前窗口句柄
Napi::Buffer<void *> buffer = info[0].As<Napi::Buffer<void *>>();
HWND hWnd = static_cast<HWND>(*reinterpret_cast<void **>(buffer.Data()));
if (!IsWindow(hWnd))
{
return Napi::Boolean::New(info.Env(), false);
}
// 获取输入法上下文
HIMC hImc = ImmGetContext(hWnd);
if (hImc == nullptr)
{
return Napi::Boolean::New(info.Env(), false);
}
// 设置输入法的首选转换模式为英文
ImmSetConversionStatus(hImc, IME_CMODE_ALPHANUMERIC, IME_SMODE_AUTOMATIC);
// 释放输入法上下文
ImmReleaseContext(hWnd, hImc);
return Napi::Boolean::New(info.Env(), true);
}
Napi::String getCursorPosWindowClassName(const Napi::CallbackInfo &info)
{
POINT cursorPos;
GetCursorPos(&cursorPos);
HWND windowHandle = WindowFromPoint(cursorPos);
char className[256];
GetClassName(windowHandle, className, sizeof(className));
std::string classNameStr(className);
return Napi::String::New(info.Env(), className);
}
Napi::Boolean TurnOffMonitor(const Napi::CallbackInfo &info)
{
// 关闭显示器
SendMessage(FindWindow(0, 0), WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
return Napi::Boolean::New(info.Env(), true);
}
Napi::Boolean EmptyRecycleBin(const Napi::CallbackInfo &info) {
// 获取当前窗口句柄
Napi::Buffer<void *> buffer = info[0].As<Napi::Buffer<void *>>();
HWND hWnd = static_cast<HWND>(*reinterpret_cast<void **>(buffer.Data()));
if (!IsWindow(hWnd))
{
return Napi::Boolean::New(info.Env(), false);
}
SHEmptyRecycleBinW(hWnd, NULL, SHERB_NOSOUND);
return Napi::Boolean::New(info.Env(), true);
}
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
exports.Set(Napi::String::New(env, "GetFileIcon"),
Napi::Function::New(env, GetFileIcon));
exports.Set(Napi::String::New(env, "GetShortcutFile"),
Napi::Function::New(env, GetShortcutFile));
exports.Set(Napi::String::New(env, "RunItem"),
Napi::Function::New(env, RunItem));
exports.Set(Napi::String::New(env, "createMouseHook"),
Napi::Function::New(env, createMouseHook));
exports.Set(Napi::String::New(env, "enableMouseMove"),
Napi::Function::New(env, enableMouseMove));
exports.Set(Napi::String::New(env, "disableMouseMove"),
Napi::Function::New(env, disableMouseMove));
exports.Set(Napi::String::New(env, "pauseMouseEvents"),
Napi::Function::New(env, pauseMouseEvents));
exports.Set(Napi::String::New(env, "resumeMouseEvents"),
Napi::Function::New(env, resumeMouseEvents));
exports.Set(Napi::String::New(env, "IsFullscreen"),
Napi::Function::New(env, IsFullscreen));
exports.Set(Napi::String::New(env, "ContextMenu"),
Napi::Function::New(env, ContextMenu));
exports.Set(Napi::String::New(env, "SwitchEnglish"),
Napi::Function::New(env, SwitchEnglish));
exports.Set(Napi::String::New(env, "getCursorPosWindowClassName"),
Napi::Function::New(env, getCursorPosWindowClassName));
exports.Set(Napi::String::New(env, "TurnOffMonitor"),
Napi::Function::New(env, TurnOffMonitor));
exports.Set(Napi::String::New(env, "EmptyRecycleBin"),
Napi::Function::New(env, EmptyRecycleBin));
return exports;
}
NODE_API_MODULE(hello, Init)

View File

@ -1,32 +0,0 @@
import { app } from "electron";
import fs from "fs";
import path from "path";
function getDawnLauncherProfilePath() {
let p;
if (process.env.NODE_ENV !== "production") {
p = path.resolve(".");
} else {
p = path.dirname(process.execPath);
}
p = path.resolve(p, "..");
p = path.join(p, ".dawn_launcher_profile");
return p;
}
try {
// 安装版
// 记录一下默认目录
global.defaultAppDataPath = app.getPath("appData");
// 获取数据目录配置文件地址
let dataDirPath = getDawnLauncherProfilePath();
// 读取文件内容
let r = fs.readFileSync(dataDirPath);
if (r != null) {
let appDataPath = r.toString();
fs.statSync(appDataPath);
app.setPath("appData", appDataPath);
}
// 免安装版
// app.setPath("appData", process.env.NODE_ENV !== "production" ? path.resolve(".") + "/data" : path.dirname(process.execPath) + "/data");
} catch (e) {}

View File

@ -1,7 +0,0 @@
import Store from "electron-store";
const cacheStore = new Store({ name: "cache", encryptionKey: "41fdb85a-4706-57b1-ba22-d7556f3723c7", clearInvalidConfig: true });
export default {
cacheStore,
};

View File

@ -1,50 +0,0 @@
import util from "../util"
/**
* 获取分类
* @param parentId
* @param childId
* @returns {*|null|{childList}|any|any}
*/
function getClassificationById(parentId, childId) {
if (parentId != null) {
let classificationParent;
for (let c of global.list) {
if (c.id == parentId) {
classificationParent = c;
break;
}
}
if (classificationParent != null && childId != null) {
if (!util.arrayIsEmpty(classificationParent.childList)) {
let classificationChild;
for (let c of classificationParent.childList) {
if (c.id == childId) {
classificationChild = c;
break;
}
}
return classificationChild;
} else {
return classificationParent;
}
} else {
return classificationParent;
}
}
return null;
}
/**
* 转换ID
* @param id
* @param parentId
*/
function convertClassificationId(id, parentId) {
return { classificationParentId: parentId != null ? parentId : id, classificationChildId: parentId != null ? id : null };
}
export default {
getClassificationById,
convertClassificationId,
};

View File

@ -1,338 +0,0 @@
import { dialog, ipcMain, Menu } from "electron";
import data from "../data";
import ItemJS from "../item/index";
import cacheData from "../cache/data";
import util from "../util";
/**
* 删除分类提示
* @param params
* @param callback
*/
function classificationDeleteDialog(params, callback) {
let name = params.classificationChildId != null ? params.classificationChildName : params.classificationParentName;
dialog
.showMessageBox(global.mainWindow, {
message: global.currentLanguage.deleteClassificationMessage,
buttons: [global.currentLanguage.ok, global.currentLanguage.cancel],
type: "question",
noLink: true,
cancelId: 1,
})
.then((r) => {
if (r.response == 0) {
callback(params);
}
});
}
export default function () {
// 分类空白处右键菜单
ipcMain.on("classificationContentRightMenu", (event, args) => {
let m = Menu.buildFromTemplate([
{
label: global.currentLanguage.newClassification,
click: () => {
let params = {
type: 0,
};
global.mainWindow.webContents.send("showClassificationAddEditWindow", JSON.stringify(params));
},
},
]);
util.menuListen(m);
m.popup();
});
// 某个父级分类上右键菜单
ipcMain.on("classificationRightMenu", (event, args) => {
let p = JSON.parse(args);
let menuList = [
{
label: global.currentLanguage.newSubClassification,
click: () => {
let params = {
type: 0,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAddEditWindow", JSON.stringify(params));
},
},
{ type: "separator" },
{
label: global.currentLanguage.newClassification,
click: () => {
let params = {
type: 0,
};
global.mainWindow.webContents.send("showClassificationAddEditWindow", JSON.stringify(params));
},
},
{ type: "separator" },
];
// 固定分类
let fixedClassificationData = data.store.get("fixedClassification");
let selected =
fixedClassificationData != null &&
fixedClassificationData.classificationParentId == p.classificationParentId &&
fixedClassificationData.classificationChildId == null;
menuList.push({
label: global.currentLanguage.fixedClassification,
icon: selected ? util.getDot() : null,
click: () => {
if (selected) {
data.store.set("fixedClassification", null);
} else {
data.store.set("fixedClassification", { classificationParentId: p.classificationParentId });
}
},
});
if (!p.aggregate) {
menuList.push({
label: global.currentLanguage.excludeSearch,
icon: p.excludeSearch ? util.getDot() : null,
click: () => {
let params = {
classificationParentId: p.classificationParentId,
};
global.mainWindow.webContents.send("classificationExcludeSearch", JSON.stringify(params));
},
});
}
menuList.push({ type: "separator" });
if (!p.haveClassificationChild) {
if (!p.aggregate) {
// 关联文件夹
menuList.push({
label: global.currentLanguage.associatedFolder,
click: () => {
let params = {
id: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAssociatedFolderWindow", JSON.stringify(params));
},
});
}
if (!p.isMapDirectory) {
menuList.push({
label: "聚合分类",
click: () => {
let params = {
id: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAggregateWindow", JSON.stringify(params));
},
});
}
menuList.push({ type: "separator" });
}
menuList.push({
label: global.currentLanguage.setIcon,
click: () => {
let params = {
id: p.classificationParentId,
};
global.mainWindow.webContents.send("showSetClassificationIconWindow", JSON.stringify(params));
},
});
menuList.push({
label: global.currentLanguage.deleteIcon,
click: () => {
let params = {
id: p.classificationParentId,
};
global.mainWindow.webContents.send("deleteSetClassificationIcon", JSON.stringify(params));
},
});
menuList.push({ type: "separator" });
if (!p.aggregate) {
menuList.push(ItemJS.itemSortMenu(p.classificationParentId, null, p.haveClassificationChild, p.sort));
}
menuList.push(...ItemJS.itemLayoutIconSize(p.classificationParentId, p.classificationChildId, p.haveClassificationChild, p.layout, p.iconSize));
menuList.push(ItemJS.itemShowOnly(p.classificationParentId, null, p.haveClassificationChild, p.showOnly));
if (
!p.haveClassificationChild &&
((p.layout != null && p.layout == "list") || (global.setting.item.layout == "list" && (p.layout == null || p.layout == "default")))
) {
menuList.push(ItemJS.itemColumnNumber(p.classificationParentId, null, p.haveClassificationChild, p.columnNumber));
}
menuList.push({ type: "separator" });
menuList.push(
{
label: global.currentLanguage.edit,
click: () => {
let params = {
type: 1,
id: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAddEditWindow", JSON.stringify(params));
},
},
{
label: global.currentLanguage.delete,
click: () => {
classificationDeleteDialog(p, (p) => {
let params = {
id: p.classificationParentId,
};
global.mainWindow.webContents.send("classificationDelete", JSON.stringify(params));
});
},
}
);
menuList.push({ type: "separator" });
menuList.push({
label: p.lockClassification ? global.currentLanguage.unlockClassification : global.currentLanguage.lockClassification,
click: () => {
global.mainWindow.webContents.send("setLockClassification", !p.lockClassification);
cacheData.cacheStore.set("lockClassification", !p.lockClassification);
},
});
let m = Menu.buildFromTemplate(menuList);
util.menuListen(m);
m.popup();
});
// 某个子级分类上右键菜单
ipcMain.on("classificationChildRightMenu", (event, args) => {
let p = JSON.parse(args);
let menuList = [];
// 固定分类
let fixedClassificationData = data.store.get("fixedClassification");
let selected =
fixedClassificationData != null &&
fixedClassificationData.classificationParentId == p.classificationParentId &&
fixedClassificationData.classificationChildId == p.classificationChildId;
menuList.push({
label: global.currentLanguage.fixedClassification,
icon: selected ? util.getDot() : null,
click: () => {
if (selected) {
data.store.set("fixedClassification", null);
} else {
data.store.set("fixedClassification", { classificationParentId: p.classificationParentId, classificationChildId: p.classificationChildId });
}
},
});
if (!p.aggregate) {
menuList.push({
label: global.currentLanguage.excludeSearch,
icon: p.excludeSearch ? util.getDot() : null,
click: () => {
let params = {
classificationParentId: p.classificationParentId,
classificationChildId: p.classificationChildId,
};
global.mainWindow.webContents.send("classificationExcludeSearch", JSON.stringify(params));
},
});
}
menuList.push({ type: "separator" });
if (!p.aggregate) {
// 关联文件夹
menuList.push({
label: global.currentLanguage.associatedFolder,
click: () => {
let params = {
id: p.classificationChildId,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAssociatedFolderWindow", JSON.stringify(params));
},
});
}
if (!p.isMapDirectory) {
menuList.push({
label: "聚合分类",
click: () => {
let params = {
id: p.classificationChildId,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAggregateWindow", JSON.stringify(params));
},
});
}
menuList.push({ type: "separator" });
menuList.push({
label: global.currentLanguage.setIcon,
click: () => {
let params = {
id: p.classificationChildId,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("showSetClassificationIconWindow", JSON.stringify(params));
},
});
menuList.push({
label: global.currentLanguage.deleteIcon,
click: () => {
let params = {
id: p.classificationChildId,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("deleteSetClassificationIcon", JSON.stringify(params));
},
});
menuList.push({ type: "separator" });
if (!p.aggregate) {
menuList.push(ItemJS.itemSortMenu(p.classificationParentId, p.classificationChildId, p.haveClassificationChild, p.sort));
}
menuList.push(...ItemJS.itemLayoutIconSize(p.classificationParentId, p.classificationChildId, p.haveClassificationChild, p.layout, p.iconSize));
menuList.push(ItemJS.itemShowOnly(p.classificationParentId, p.classificationChildId, p.haveClassificationChild, p.showOnly));
if (
!p.haveClassificationChild &&
((p.layout != null && p.layout == "list") || (global.setting.item.layout == "list" && (p.layout == null || p.layout == "default")))
) {
menuList.push(ItemJS.itemColumnNumber(p.classificationParentId, p.classificationChildId, p.haveClassificationChild, p.columnNumber));
}
menuList.push({ type: "separator" });
menuList.push(
{
label: global.currentLanguage.edit,
click: () => {
let params = {
type: 1,
id: p.classificationChildId,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("showClassificationAddEditWindow", JSON.stringify(params));
},
},
{
label: global.currentLanguage.delete,
click: () => {
classificationDeleteDialog(p, (p) => {
let params = {
id: p.classificationChildId,
parentId: p.classificationParentId,
};
global.mainWindow.webContents.send("classificationDelete", JSON.stringify(params));
});
},
}
);
menuList.push({ type: "separator" });
menuList.push({
label: p.lockClassification ? global.currentLanguage.unlockClassification : global.currentLanguage.lockClassification,
click: () => {
global.mainWindow.webContents.send("setLockClassification", !p.lockClassification);
cacheData.cacheStore.set("lockClassification", !p.lockClassification);
},
});
let m = Menu.buildFromTemplate(menuList);
util.menuListen(m);
m.popup();
});
// 获取锁定分类状态
ipcMain.on("getLockClassification", (event, args) => {
let lockClassification = cacheData.cacheStore.get("lockClassification");
event.returnValue = lockClassification == null ? false : lockClassification;
});
// 获取固定分类
ipcMain.on("getFixedClassification", (event, args) => {
event.returnValue = data.store.get("fixedClassification");
});
// 设置固定分类
ipcMain.on("setFixedClassification", (event, args) => {
data.store.set("fixedClassification", args);
});
}

View File

@ -1,126 +0,0 @@
import ClassificationJS from "./classification/index";
const Store = require("electron-store");
const store = new Store({ name: "data", encryptionKey: "0b52eb58-4c0f-5ff1-b062-031546a8d269", clearInvalidConfig: true });
/**
* 默认初始化
* @returns {Promise<void>}
*/
function initData() {
let list = store.get("list");
if (list == null || list.length == 0) {
list = [
{
id: 1,
name: "新分类",
order: 0,
},
];
store.set("list", list);
}
global.list = list;
}
/**
* 分离数据
*/
function splitData() {
let iconData = store.get("iconData");
if (iconData == null) {
iconData = [];
for (let c of global.list) {
if (c.childList != null) {
for (let cc of c.childList) {
if (cc.itemList != null) {
for (let item of cc.itemList) {
let icon = {
classificationParentId: item.classificationParentId,
classificationChildId: item.classificationId,
itemId: item.id,
icon: item.icon,
};
item.icon = null;
iconData.push(icon);
}
}
}
} else {
if (c.itemList != null) {
for (let item of c.itemList) {
let icon = {
classificationParentId: item.classificationId,
classificationChildId: null,
itemId: item.id,
icon: item.icon,
};
item.icon = null;
iconData.push(icon);
}
}
}
}
store.set("iconData", iconData);
store.set("list", global.list);
}
}
/**
* 校验数据
*/
function validData() {
let iconData = store.get("iconData");
if (iconData != null) {
let newIconData = [];
for (let icon of iconData) {
let classification = ClassificationJS.getClassificationById(icon.classificationParentId, icon.classificationChildId);
if (classification != null && classification.itemList != null && classification.itemList.length > 0) {
for (let item of classification.itemList) {
if (icon.itemId == item.id) {
newIconData.push(icon);
break;
}
}
}
}
store.set("iconData", newIconData);
}
}
/**
* 获取分类数据
* @returns {Promise<T[string]>}
*/
function getList() {
this.initData();
let list = store.get("list");
global.list = list;
return list;
}
/**
* 保存
* @param list
* @returns {Promise<void>}
*/
function setList(list) {
store.set("list", list);
global.list = list;
}
/**
* 获取图标
* @returns {Promise<T[string]>}
*/
function getIconData() {
return store.get("iconData");
}
export default {
store,
initData,
splitData,
validData,
getList,
setList,
getIconData,
};

View File

@ -1,414 +0,0 @@
import { ipcMain, dialog, Menu, app, shell } from "electron";
import os from "os";
import data from "@/main/data";
import fs from "fs";
import settingIndex from "./setting/index";
import util from "./util";
import retry from "retry";
import request from "request";
import mime from "mime";
import path from "path";
/**
* 固定位置
* @param fixedPosition
* @param alwaysCenter
*/
function setFixedPosition(fixedPosition, alwaysCenter) {
global.mainWindow.setMovable(fixedPosition);
if (alwaysCenter) {
global.mainWindow.setMovable(false);
}
}
export default function () {
// 隐藏
ipcMain.on("hide", (event, args) => {
global.mainWindow.hide();
});
// 隐藏
ipcMain.on("hideMainWindow", (event, args) => {
global.mainWindow.webContents.send("hideMainWindowBefore");
});
// 关闭
ipcMain.on("close", (event, args) => {
global.mainWindow.close();
});
// 获取数据
ipcMain.on("getList", (event) => {
let list = data.getList();
event.returnValue = list;
});
// 保存数据
ipcMain.on("setList", (event, args) => {
let params = JSON.parse(args);
data.setList(params.list);
settingIndex.setShortcutKey(global.setting);
if (params.searchWindowGetData != null && params.searchWindowGetData) {
if (global.searchWindow != null && !global.searchWindow.isDestroyed()) {
global.searchWindow.webContents.send("searchWindowGetData");
}
}
});
// 错误消息
ipcMain.on("errorMessage", (event, args) => {
dialog.showMessageBox(global.mainWindow, {
title: "Dawn Launcher",
message: args,
buttons: [global.currentLanguage.ok],
type: "error",
noLink: true,
cancelId: 1,
});
});
// 文本框菜单
ipcMain.on("textRightMenu", (event, args) => {
// 菜单
let m = Menu.buildFromTemplate([
{
role: "cut",
label: global.currentLanguage.cut,
},
{
role: "copy",
label: global.currentLanguage.copy,
},
{
role: "paste",
label: global.currentLanguage.paste,
},
]);
util.menuListen(m);
m.popup();
});
// 获取版本
ipcMain.on("getVersion", (event) => {
event.returnValue = app.getVersion();
});
// 打开网页
ipcMain.on("openUrl", (event, args) => {
shell.openExternal(args);
});
// 检查更新
ipcMain.on("checkUpdate", () => {
util.checkUpdate("checkUpdate");
});
// 统计
ipcMain.on("statistics", () => {
try {
let data = {
system: os.type(),
release: os.release(),
locale: app.getLocale(),
appVersion: app.getVersion(),
};
// 重试
const operation = retry.operation({
retries: 5, // 最多重试 5 次
factor: 1, // 每次重试之间的时间间隔加倍
minTimeout: 1000, // 第一次重试之前等待的时间
maxTimeout: 5000, // 最长等待时间
});
// 发起请求
operation.attempt((currentAttempt) => {
request(
{
uri: "https://client.dawnlauncher.com/access/statistics/add",
method: "POST",
json: true,
headers: {
"content-type": "application/json",
},
body: data,
timeout: 5000,
},
function (error, response, body) {
if (operation.retry(error)) {
return;
}
}
);
});
} catch (e) {}
});
// 备份数据
ipcMain.on("backup", () => {
try {
dialog
.showSaveDialog(global.mainWindow, {
title: global.currentLanguage.backUpData,
defaultPath: "data",
filters: [{ name: "JSON", extensions: ["json"] }],
})
.then((r) => {
if (!r.canceled && !util.strIsEmpty(r.filePath)) {
fs.copyFileSync(app.getPath("userData") + "\\data.json", r.filePath);
global.mainWindow.webContents.send("hideBackupRestore");
}
});
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
});
// 恢复数据
ipcMain.on("restore", () => {
try {
dialog
.showOpenDialog(global.mainWindow, {
title: global.currentLanguage.restoreData,
filters: [{ name: "JSON", extensions: ["json"] }],
})
.then((r) => {
if (!r.canceled && !util.arrayIsEmpty(r.filePaths)) {
if (!util.strIsEmpty(r.filePaths[0])) {
fs.copyFileSync(r.filePaths[0], app.getPath("userData") + "\\data.json");
// 清空所有文件映射监听
if (global.mapDirectoryWatcher != null) {
for (let value of global.mapDirectoryWatcher.values()) {
if (value != null && value.watch != null && value.watch) {
value.watch.close();
}
}
global.mapDirectoryWatcher = new Map();
}
// 初始化图标数据
data.getList();
data.splitData();
// 重新获取数据
global.mainWindow.webContents.send("getAllData");
}
}
});
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
});
// 设置置顶
ipcMain.on("setAlwaysTop", (event, args) => {
if (args) {
global.mainWindow.setAlwaysOnTop(true, "screen-saver");
} else {
global.mainWindow.setAlwaysOnTop(false);
}
});
// 反馈
ipcMain.on("feedback", () => {
shell.openExternal("https://support.qq.com/product/487828");
});
// 锁定尺寸
ipcMain.on("setResize", (event, args) => {
global.mainWindow.setResizable(args);
});
// 设置透明度
ipcMain.on("setOpacity", (event, args) => {
global.mainWindow.setOpacity(Number(args));
});
// 设置固定位置
ipcMain.on("setFixedPosition", (event, args) => {
setFixedPosition(args[0], args[1]);
});
// 永远居中
ipcMain.on("setAlwaysCenter", (event, args) => {
if (args[0]) {
global.mainWindow.center();
global.mainWindow.setMovable(false);
} else {
setFixedPosition(args[1], args[2]);
}
});
// 打赏赞助
ipcMain.on("rewardAndSponsorship", () => {
shell.openExternal("https://dawnlauncher.com/sponsor");
});
// 跳转搜索窗口高度
ipcMain.on("setSearchWindowHeight", (event, args) => {
global.searchWindow.setBounds({ height: args });
});
// 隐藏搜索窗口
ipcMain.on("hideSearchWindow", () => {
if (global.searchWindow.isVisible()) {
global.searchWindow.hide();
}
});
// 窗口设置透明
ipcMain.on("setSearchWindowOpacity", (event, args) => {
global.searchWindow.setOpacity(args);
event.returnValue = null;
});
// 获取release
ipcMain.on("getRelease", (event, args) => {
event.returnValue = os.release();
});
// 获取背景图
ipcMain.on("getBackgroundImageBase64", (event, args) => {
let params = JSON.parse(args);
fs.readFile(app.getPath("userData") + "\\images\\" + params.backgroundImage, (err, data) => {
if (!err) {
try {
let buffer = Buffer.from(data);
let image = "data:" + mime.getType(params.backgroundImage) + ";base64," + buffer.toString("base64");
if (params.page == "main") {
global.mainWindow.webContents.send("returnBackgroundImageBase64", image);
} else {
global.settingWindow.webContents.send("returnBackgroundImageBase64", image);
}
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
}
});
});
// 通知快速搜索窗口获取数据
ipcMain.on("noticeSearchWindowGetData", () => {
if (global.searchWindow != null) {
global.searchWindow.webContents.send("searchWindowGetData");
}
});
// 获取软件目录
ipcMain.on("getPath", (event, args) => {
event.returnValue = process.env.NODE_ENV !== "production" ? path.resolve(".") : path.dirname(process.execPath);
});
// 获取图标
ipcMain.on("getIconData", (event) => {
let iconData = data.getIconData();
event.returnValue = iconData;
});
// 更新图标
ipcMain.on("updateIconData", (event, args) => {
// 参数
let updateIconData = JSON.parse(args);
// 获取图标数据
let iconData = data.store.get("iconData");
if (iconData != null) {
// 删除
if (!util.arrayIsEmpty(updateIconData.delete)) {
for (let del of updateIconData.delete) {
let index;
for (let i = 0; i < iconData.length; i++) {
if (
iconData[i].classificationParentId == del.classificationParentId &&
iconData[i].classificationChildId == del.classificationChildId &&
iconData[i].itemId == del.itemId
) {
index = i;
break;
}
}
if (index != null) {
iconData.splice(index, 1);
}
}
}
// 添加
if (!util.arrayIsEmpty(updateIconData.add)) {
for (let add of updateIconData.add) {
let icon = {
classificationParentId: add.classificationParentId,
classificationChildId: add.classificationChildId,
itemId: add.itemId,
icon: add.icon,
};
iconData.push(icon);
}
}
// 更新
if (!util.arrayIsEmpty(updateIconData.update)) {
for (let update of updateIconData.update) {
let flag = false;
for (let icon of iconData) {
if (
icon.classificationParentId == update.classificationParentId &&
icon.classificationChildId == update.classificationChildId &&
icon.itemId == update.itemId
) {
icon.icon = update.icon;
flag = true;
break;
}
}
if (!flag) {
iconData.push(update);
}
}
}
// set
data.store.set("iconData", iconData);
}
// 更新搜索框图标数据
if (global.searchWindow != null && !global.searchWindow.isDestroyed()) {
global.searchWindow.webContents.send("searchWindowUpdateIconData", args);
}
});
// 通知搜索窗口重新获取图标数据
ipcMain.on("searchWindowGetIconData", (event, args) => {
if (global.searchWindow != null && !global.searchWindow.isDestroyed()) {
global.searchWindow.webContents.send("getIconData");
}
});
// showMessageBoxSync
ipcMain.on("showMessageBoxSync", (event, args) => {
let index = dialog.showMessageBoxSync(global.mainWindow, {
title: "Dawn Launcher",
message: args,
buttons: [global.currentLanguage.ok, global.currentLanguage.cancel],
type: "question",
noLink: true,
cancelId: 1,
});
event.returnValue = index == 0 ? true : false;
});
// 选择文件夹
ipcMain.on("openDirectory", (event, args) => {
let params = JSON.parse(args);
let options = {
properties: ["openDirectory"],
};
if (!util.strIsEmpty(params.defaultPath)) {
options.defaultPath = params.defaultPath;
} else {
options.defaultPath = app.getPath("desktop");
}
dialog.showOpenDialog(params.window == "mainWindow" ? global.mainWindow : null, options).then((r) => {
if (r.filePaths.length > 0) {
event.returnValue = r.filePaths[0];
} else {
event.returnValue = null;
}
});
});
// 选择文件
ipcMain.on("openFile", (event, args) => {
let params = JSON.parse(args);
let options = {};
if (!util.strIsEmpty(params.defaultPath)) {
options.defaultPath = params.defaultPath;
} else {
options.defaultPath = app.getPath("desktop");
}
dialog.showOpenDialog(params.window == "mainWindow" ? global.mainWindow : null, options).then((r) => {
if (r.filePaths.length > 0) {
let filePath = r.filePaths[0];
if (params.target) {
if (mime.getType(filePath) == "application/x-ms-shortcut") {
// 快捷方式
// 获取真实文件路径和参数
let shortcutDetail = global.api.GetShortcutFile(filePath);
if (!util.strIsEmpty(shortcutDetail.target)) {
// 路径
filePath = shortcutDetail.target;
}
}
}
event.returnValue = filePath;
} else {
event.returnValue = null;
}
});
});
}

View File

@ -1,790 +0,0 @@
import { shell, dialog, app } from "electron";
import path from "path";
import fs from "fs";
import util from "../util";
import { v4 } from "uuid";
import Jimp from "jimp";
import ClassificationJS from "../classification/index.js";
import data from "../data";
const { exec } = require("child_process");
/**
* 校验无效项目
* @param itemList
*/
function checkInvalidItemList(itemList) {
let list = [];
if (!util.arrayIsEmpty(itemList)) {
for (let item of itemList) {
// 只校验文件和文件夹
if (item.type == 0 || item.type == 1) {
// 获取绝对路径
item.path = getAbsolutePath(item.path);
try {
fs.statSync(item.path);
} catch (e) {
if (item.classificationParentId != null) {
list.push(item.classificationParentId + "-" + item.classificationId + "-" + item.id);
} else {
list.push(item.classificationId + "-" + item.id);
}
}
}
}
}
return list;
}
/**
* 校验无效项目
* @returns {*[]}
*/
function checkInvalidItem() {
let list = [];
for (let c of global.list) {
if (!util.arrayIsEmpty(c.childList)) {
for (let cc of c.childList) {
if (util.strIsEmpty(cc.mapDirectory)) {
list.push(...checkInvalidItemList(cc.itemList));
}
}
} else {
if (util.strIsEmpty(c.mapDirectory)) {
list.push(...checkInvalidItemList(c.itemList));
}
}
}
return list;
}
/**
* 解析环境变量
* @param p
*/
function parseEnvPath(p) {
// 尝试解析环境变量
let parsedPath = path.parse(p);
let isBase = false;
let dirArr;
if (util.strIsEmpty(parsedPath.dir)) {
dirArr = parsedPath.base.split("\\");
isBase = true;
} else {
dirArr = parsedPath.dir.split("\\");
}
let newPathArr = [];
const pattern = /^%.*%$/;
for (let string of dirArr) {
if (pattern.test(string)) {
let nString = string.substring(1, string.length - 1);
if (!util.strIsEmpty(process.env[nString])) {
newPathArr.push(process.env[nString]);
} else {
newPathArr.push(string);
}
} else {
newPathArr.push(string);
}
}
if (!isBase) {
newPathArr.push(parsedPath.base);
}
return newPathArr.join("\\");
}
/**
* 是否是绝对路径
* @param p
*/
function isAbsolutePath(p) {
const regex = /^[a-zA-Z]:\\/;
return regex.test(p);
}
/**
* 获取绝对路径
* @param path
*/
function getAbsolutePath(p) {
if (!isAbsolutePath(p)) {
// 尝试解析环境变量
let newPath = parseEnvPath(p);
// 判断解析之后的路径是否是绝对路径
if (isAbsolutePath(newPath)) {
return newPath;
} else {
return path.resolve(process.env.NODE_ENV !== "production" ? path.resolve(".") : path.dirname(process.execPath), p);
}
}
return p;
}
/**
* 运行项目
* @param item
* @param location 是否打开文件所在的位置
* @param openWith 打开方式
*/
function itemRun(item, location, openWith) {
// 系统
if (item.type == 3) {
if (item.shell.indexOf("shell:") >= 0) {
shell.openExternal(item.shell);
} else {
if (item.shell == "cmd") {
if (item.admin) {
exec('powershell -Command "Start-Process cmd -Verb RunAs"', (error, stdout, stderr) => {});
} else {
exec("start cmd.exe", (error, stdout, stderr) => {});
}
} else if (item.shell == "turnOffMonitor") {
global.api.TurnOffMonitor();
} else {
exec(item.shell, (error, stdout, stderr) => {});
}
}
return;
} else if (item.type == 5) {
exec("start " + item.shell, (error, stdout, stderr) => {});
return;
}
// 如果是类型是0或者1并且是相对路径的话恢复为绝对路径
if (item.type == 0 || item.type == 1) {
// 获取路径
item.path = getAbsolutePath(item.path);
}
let t = item.path;
let params = !util.strIsEmpty(item.params) ? item.params.trim() : "";
if (openWith != null && openWith && item.type == 0) {
exec("RUNDLL32.EXE SHELL32.DLL,OpenAs_RunDLL " + t + "", (error, stdout, stderr) => {});
} else {
let type = "open";
if (item.type == 0) {
if (location) {
// 如果是打开文件所在位置
exec('start %windir%\\explorer.exe /select, "' + item.path + '"', (error, stdout, stderr) => {});
return;
} else {
// 以管理员身份运行
if (item.admin && (item.extension == ".exe" || item.extension == ".bat")) {
type = "runas";
}
}
} else if (item.type == 2) {
// 网址
t = item.url;
}
if (item.type == 0 || item.type == 1) {
// 判断文件或文件夹是否存在
try {
fs.accessSync(t);
global.api.RunItem(type, t, params, item.type == 0 && !util.strIsEmpty(item.startLocation) ? item.startLocation : path.dirname(item.path));
} catch (e) {
let message;
if (item.type == 0 && !location) {
message = global.currentLanguage.notFoundFileMessage;
} else {
message = global.currentLanguage.notFoundFolderMessage;
}
message += '"' + t + '"。';
dialog.showMessageBox(global.mainWindow, {
title: "Dawn Launcher",
message: message,
buttons: [global.currentLanguage.ok],
type: "error",
noLink: true,
});
}
} else {
global.api.RunItem(type, t, params, null);
}
}
}
/**
* 返回排序菜单
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param sort
*/
function itemSortMenu(classificationParentId, classificationChildId, haveClassificationChild, sort) {
let submenu = [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
sort: "default",
};
global.mainWindow.webContents.send("itemSort", JSON.stringify(params));
},
icon: (sort == null || sort == "default") && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.byInitial,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
sort: "initial",
};
global.mainWindow.webContents.send("itemSort", JSON.stringify(params));
},
icon: sort != null && sort == "initial" ? util.getDot() : null,
},
];
if (global.setting.item.openNumber) {
submenu.push({
label: global.currentLanguage.byOpenNumber,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
sort: "openNumber",
};
global.mainWindow.webContents.send("itemSort", JSON.stringify(params));
},
icon: sort != null && sort == "openNumber" ? util.getDot() : null,
});
}
return {
label: global.currentLanguage.sort,
type: "submenu",
submenu: submenu,
};
}
/**
* 返回布局和图标大小
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param layout
* @param iconSize
*/
function itemLayoutIconSize(classificationParentId, classificationChildId, haveClassificationChild, layout, iconSize) {
let menuList = [
{
label: global.currentLanguage.layout,
type: "submenu",
submenu: [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: "default",
};
global.mainWindow.webContents.send("itemTile", JSON.stringify(params));
},
icon: (layout == null || layout == "default") && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.tile,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: "tile",
};
global.mainWindow.webContents.send("itemTile", JSON.stringify(params));
},
icon: layout != null && layout == "tile" ? util.getDot() : null,
},
{
label: global.currentLanguage.list,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: "list",
};
global.mainWindow.webContents.send("itemTile", JSON.stringify(params));
},
icon: layout != null && layout == "list" ? util.getDot() : null,
},
],
},
];
menuList.push({
label: global.currentLanguage.iconSize,
type: "submenu",
submenu: [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: null,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize == null && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.extraLarge,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 48,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 48 ? util.getDot() : null,
},
{
label: global.currentLanguage.large,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 40,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 40 ? util.getDot() : null,
},
{
label: global.currentLanguage.medium,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 32,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 32 ? util.getDot() : null,
},
{
label: global.currentLanguage.small,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
type: 24,
};
global.mainWindow.webContents.send("itemIconSize", JSON.stringify(params));
},
icon: iconSize != null && iconSize == 24 ? util.getDot() : null,
},
],
});
return menuList;
}
/**
* 返回显示菜单
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param showOnly
*/
function itemShowOnly(classificationParentId, classificationChildId, haveClassificationChild, showOnly) {
let submenu = [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
showOnly: "default",
};
global.mainWindow.webContents.send("itemShowOnly", JSON.stringify(params));
},
icon: (showOnly == null || showOnly == "default") && !haveClassificationChild ? util.getDot() : null,
},
{
label: global.currentLanguage.showOnlyFiles,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
showOnly: "file",
};
global.mainWindow.webContents.send("itemShowOnly", JSON.stringify(params));
},
icon: showOnly != null && showOnly == "file" ? util.getDot() : null,
},
{
label: global.currentLanguage.showOnlyFolders,
type: "normal",
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
showOnly: "folder",
};
global.mainWindow.webContents.send("itemShowOnly", JSON.stringify(params));
},
icon: showOnly != null && showOnly == "folder" ? util.getDot() : null,
},
];
return {
label: global.currentLanguage.show,
type: "submenu",
submenu: submenu,
};
}
/**
* 返回列数菜单
* @param classificationParentId
* @param classificationChildId
* @param haveClassificationChild
* @param columnNumber
*/
function itemColumnNumber(classificationParentId, classificationChildId, haveClassificationChild, columnNumber) {
let submenu = [
{
label: global.currentLanguage.default,
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
columnNumber: 0,
};
global.mainWindow.webContents.send("itemColumnNumber", JSON.stringify(params));
},
icon: (columnNumber == null || columnNumber == 0) && !haveClassificationChild ? util.getDot() : null,
},
];
for (let i = 0; i < 20; i++) {
submenu.push({
label: (i + 1).toString(),
click: () => {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
columnNumber: i + 1,
};
global.mainWindow.webContents.send("itemColumnNumber", JSON.stringify(params));
},
icon: columnNumber != null && columnNumber == i + 1 ? util.getDot() : null,
});
}
return {
label: global.currentLanguage.numberOfColumns,
type: "submenu",
submenu: submenu,
};
}
/**
* 获取文件图标
* @param target
* @param message
*/
async function getFileIcon(target, message) {
// 获取绝对路径
target = getAbsolutePath(target);
let size = 256;
try {
// 先获取一下文件判断是否存在,不存在抛出异常
let stats = fs.statSync(target);
// 图标临时地址
let tempPath = app.getPath("temp") + "\\" + v4() + ".png";
// 获取图标
let result = global.api.GetFileIcon(target, tempPath, size);
// 1为成功
if (result == 1) {
// 读取图标文件
let buffer = fs.readFileSync(tempPath);
// 如果透明区域占比大于80代表这个图标没有超大图标那么就获取48*48图标
let tempResult = await Jimp.read(tempPath);
let zero = 0;
let color = 0;
for (const { x, y, image } of tempResult.scanIterator(0, 0, tempResult.bitmap.width, tempResult.bitmap.height)) {
if (image.getPixelColor(x, y) == 0) {
zero++;
} else {
color++;
}
}
// 计算占比
let proportion = Math.round((zero / (zero + color)) * 100);
// 删除临时文件
fs.unlink(tempPath, (err) => {});
// 透明区域大于80获取48*48图标
if (proportion >= 80) {
// 图标临时地址
tempPath = app.getPath("temp") + "\\" + v4() + ".png";
// 获取48*48图标
result = global.api.GetFileIcon(target, tempPath, 48);
// 1成功
if (result == 1) {
// 读取图标文件
buffer = fs.readFileSync(tempPath);
// 删除文件
fs.unlink(tempPath, (err) => {});
}
}
// 图标
let base64 = "data:image/png;base64," + buffer.toString("base64");
// 返回base64
return base64;
}
} catch (e) {
if (message) {
dialog.showMessageBox(global.mainWindow, {
title: "Dawn Launcher",
message: global.currentLanguage.targetNotExist + '"' + target + '"。',
buttons: [global.currentLanguage.ok],
type: "error",
noLink: true,
cancelId: 1,
});
}
}
return null;
}
/**
* 新建文件夹监听
*/
function addMapDirectoryWatcher(classificationParentId, classificationChildId, mapDirectory) {
// key
let key = classificationParentId + (classificationChildId != null ? "-" + classificationChildId : "");
// 先删除原有监听
deleteMapDirectoryWatcher(classificationParentId, classificationChildId);
// 新建监听
let data = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
mapDirectory: mapDirectory,
};
let timer;
let watch = fs.watch(mapDirectory, (event, filename) => {
if (timer) {
clearTimeout(timer);
timer = null;
}
// 启动定时器,在指定的时间间隔后发送合并后的通知
timer = setTimeout(() => {
readMapDirectory(classificationParentId, classificationChildId, mapDirectory, false, true, true);
clearTimeout(timer);
timer = null;
}, 1000);
});
watch.on("error", (error) => {
watch.close();
global.mapDirectoryWatcher.delete(key);
});
// 保存
data.watch = watch;
global.mapDirectoryWatcher.set(key, data);
}
/**
* 删除文件夹监听
*/
function deleteMapDirectoryWatcher(classificationParentId, classificationChildId) {
// 判断是否存在
let key = classificationParentId + (classificationChildId != null ? "-" + classificationChildId : "");
let watcherData = global.mapDirectoryWatcher.get(key);
if (watcherData != null) {
// 存在
if (watcherData.watch != null && watcherData.watch) {
watcherData.watch.close();
watcherData.watch = null;
}
global.mapDirectoryWatcher.delete(key);
}
}
/**
* 读取映射文件夹内容
* @param classificationParentId
* @param classificationChildId
* @param mapDirectory
* @param listener
* @param old
* @param notice
*/
async function readMapDirectory(classificationParentId, classificationChildId, mapDirectory, listener, old, notice) {
let itemList = [];
try {
// 判断是否含有环境变量
mapDirectory = parseEnvPath(mapDirectory);
// 获取图标数据
let iconData = data.store.get("iconData");
let iconDataMap = new Map();
// 转为Map
if (!util.arrayIsEmpty(iconData)) {
for (let icon of iconData) {
iconDataMap.set(util.getKey(icon.classificationParentId, icon.classificationChildId, icon.itemId), icon);
}
}
// 文件类型
let stats = fs.statSync(mapDirectory);
// 必须是文件夹
if (stats.isDirectory()) {
// 获取旧列表
let oldItemList = [];
let hiddenItem;
// 分类
let classification = ClassificationJS.getClassificationById(classificationParentId, classificationChildId);
if (classification != null) {
if (old != null && old) {
oldItemList = classification.itemList;
}
hiddenItem = classification.hiddenItem;
}
// 转为数组
let hiddenItemArr = [];
if (!util.strIsEmpty(hiddenItem)) {
hiddenItemArr = hiddenItem.split(",");
}
// 读取文件夹下面的所有文件
let pathList = fs.readdirSync(mapDirectory);
let i = 1;
for (let p of pathList) {
try {
// 判断是否隐藏
let flag = false;
for (let item of hiddenItemArr) {
if (item != null && p == item.trim()) {
flag = true;
break;
}
}
if (flag) {
continue;
}
// 组合路径
let np = path.join(mapDirectory, p);
// 获取类型 0:文件 1:文件夹
let type = fs.statSync(np).isDirectory() ? 1 : 0;
// 如果旧数据有的话,获取旧数据
let icon;
let openNumber;
let lastOpen;
let quickSearchOpenNumber;
let quickSearchLastOpen;
if (!util.arrayIsEmpty(oldItemList)) {
for (let oldItem of oldItemList) {
if (
(type == 0 && oldItem.name == util.removeSuffix(p) && oldItem.path == np && oldItem.type == type) ||
(type == 1 && oldItem.name == p && oldItem.path == np && oldItem.type == type)
) {
// 通过旧数据获取图标
let oldIcon = iconDataMap.get(util.getKey(classificationParentId, classificationChildId, oldItem.id));
if (oldIcon != null) {
icon = oldIcon.icon;
}
openNumber = oldItem.openNumber;
lastOpen = oldItem.lastOpen;
quickSearchOpenNumber = oldItem.quickSearchOpenNumber;
quickSearchLastOpen = oldItem.quickSearchLastOpen;
break;
}
}
}
let item = {
// id
id: i++,
// 路径
path: np,
// 名称
name: type == 1 ? p : util.removeSuffix(p),
// 全称
fullName: p,
// 图标
icon: icon != null ? icon : await getFileIcon(np, false),
// 类型 0:文件 1:文件夹
type: type,
// 打开次数
openNumber: openNumber,
// 最近打开
lastOpen: lastOpen,
// 快速搜索 打开次数
quickSearchOpenNumber: quickSearchOpenNumber,
// 快速搜索 最近打开
quickSearchLastOpen: quickSearchLastOpen,
};
itemList.push(item);
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
}
if (listener != null && listener) {
// 新建监听
addMapDirectoryWatcher(classificationParentId, classificationChildId, mapDirectory);
}
if (notice) {
let params = {
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
itemList: itemList,
clear: true,
};
// 添加项目
global.mainWindow.webContents.send("itemAdd", JSON.stringify(params));
}
}
} catch (e) {
if (process.env.NODE_ENV !== "production") {
console.log(e);
}
}
return itemList;
}
/**
* 初始化映射文件夹
*/
async function initMapDirectory() {
let list = [];
for (let c of global.list) {
if (util.arrayIsEmpty(c.childList)) {
// 没有子级
if (!util.strIsEmpty(c.mapDirectory)) {
let { classificationParentId, classificationChildId } = ClassificationJS.convertClassificationId(c.id, c.parentId);
let itemList = await readMapDirectory(classificationParentId, classificationChildId, c.mapDirectory, true, true, false);
list.push({
classificationParentId: classificationParentId,
itemList: itemList,
});
}
} else {
for (let cc of c.childList) {
if (!util.strIsEmpty(cc.mapDirectory)) {
let { classificationParentId, classificationChildId } = ClassificationJS.convertClassificationId(cc.id, cc.parentId);
let itemList = await readMapDirectory(classificationParentId, classificationChildId, cc.mapDirectory, true, true, false);
list.push({
classificationParentId: classificationParentId,
classificationChildId: classificationChildId,
itemList: itemList,
});
}
}
}
}
return list;
}
export default {
checkInvalidItem,
isAbsolutePath,
getAbsolutePath,
itemRun,
itemSortMenu,
itemLayoutIconSize,
itemShowOnly,
itemColumnNumber,
getFileIcon,
addMapDirectoryWatcher,
readMapDirectory,
deleteMapDirectoryWatcher,
initMapDirectory,
};

Some files were not shown because too many files have changed in this diff Show More