Compare commits

...

121 Commits
1.2.3 ... main

Author SHA1 Message Date
unknown
5800cbbdaa 更新版本。 2025-03-18 08:36:36 +08:00
unknown
56517f059c 优化拖拽文件速度。 2025-03-16 17:58:44 +08:00
unknown
342d8939ab 优化。 2025-03-16 17:49:04 +08:00
unknown
92270def83 优化主界面搜索框,隐藏窗口时同时隐藏搜索框。 2025-03-16 17:41:46 +08:00
unknown
881fbc9f56 修复'文件夹项目'带有特殊符号时,打开文件夹失败的问题。 2025-03-16 17:40:05 +08:00
unknown
2cd721263e 修复'任务栏'右键关闭窗口时,程序并没有完全退出的问题。 2025-03-16 17:39:09 +08:00
unknown
ea851c652c update. 2025-01-12 12:09:21 +08:00
unknown
9243140d3b 更新版本。 2025-01-12 12:08:56 +08:00
unknown
36b77ce32b 修复同一目录下打开文件夹却打开应用程序的问题。 2025-01-12 12:08:37 +08:00
unknown
d51a5404ee 修复项目在自定义排序拖拽移动项目问题。 2025-01-12 12:06:26 +08:00
unknown
018bd91931 修复程序在某些情况崩溃的问题。 2025-01-12 12:04:14 +08:00
unknown
d4520eef0c 修复某些情况下'以管理员身份启动'没有生效的问题、优化'新增/编辑项目'时'以管理员身份运行'显示逻辑。 2025-01-01 11:48:09 +08:00
unknown
0c2ae8ae5b 修复'失去焦点隐藏'有时无效的问题。 2024-11-09 16:39:26 +08:00
unknown
07912ea276 修复批量删除项目失败的问题。 2024-11-09 16:27:58 +08:00
unknown
bebe8272cf 更新版本。 2024-09-24 20:04:44 +08:00
unknown
4e3947ab7e 修复'锁定尺寸'在某些状况下没有成功的问题。 2024-09-22 22:49:07 +08:00
unknown
5c791874c6 修复'透明窗口'下出现窗口闪烁的问题。 2024-09-22 22:42:25 +08:00
unknown
a2ccd8670b 所有文本框支持右键剪切、复制、粘贴。 2024-09-22 22:13:15 +08:00
unknown
78cf39bec7 修改README。 2024-09-20 19:55:56 +08:00
unknown
62a1780197 更新版本。 2024-09-13 21:28:52 +08:00
unknown
dd3cf64f5d 优化语言。 2024-09-13 21:16:36 +08:00
unknown
ed77cc4fb4 优化批量删除项目出现卡顿的问题。 2024-09-13 21:06:19 +08:00
unknown
0327dfc0cc 优化部分网址获取信息失败的问题。 2024-09-13 21:03:20 +08:00
unknown
0ad813a315 优化右键菜单'以管理员身份运行'功能,改为右键时动态判断文件是否有此权限。 2024-09-13 20:57:27 +08:00
unknown
2ba01403a5 优化右键菜单'以管理员身份运行'功能,改为右键时动态判断文件是否有此权限。 2024-09-13 20:54:36 +08:00
unknown
f365005bd8 优化样式。 2024-09-13 20:47:09 +08:00
unknown
3ffbd110dc 优化语言。 2024-09-13 20:45:13 +08:00
unknown
7ec78fef08 首次打开程序将根据计算机语言显示 Dawn Launcher 语言。 2024-09-13 20:26:40 +08:00
unknown
b9ac69cf22 更新版本。 2024-08-29 22:47:01 +08:00
unknown
c23e4f51f0 修复关联文件夹、聚合分类下右键项目菜单显示多余选项问题。 2024-08-29 22:43:47 +08:00
unknown
a411674e3a 优化分类名称样式。 2024-08-29 22:25:10 +08:00
unknown
e1f896e963 优化窗口失去焦点隐藏、停靠在桌面边缘时自动隐藏功能,在弹出对话框时应不隐藏窗口。 2024-08-29 22:22:01 +08:00
unknown
8228495918 更新版本。 2024-07-05 22:42:47 +08:00
unknown
bcc5ad1989 修复快速搜索历史记录按打开次数排序问题。 2024-07-05 22:42:32 +08:00
unknown
dd06149fb4 优化窗口透明度操作逻辑。 2024-07-05 22:41:00 +08:00
unknown
0cb66ffaa5 修复项目隐藏省略号和项目行高冲突的问题。 2024-07-05 22:36:15 +08:00
unknown
6386518171 修复部分ico图标无效的问题。 2024-07-05 22:34:22 +08:00
unknown
49124e7eeb 修复子分类相关排序问题。 2024-07-05 22:27:34 +08:00
unknown
ac96cfd6be 优化项目宽度对齐,所有项目统一对齐。 2024-07-05 22:26:18 +08:00
unknown
950cff7a6c 优化样式。 2024-07-05 22:25:24 +08:00
unknown
f0eeea8483 修复(UNC)网络路径被识别成相对路径的问题。 2024-07-05 22:23:10 +08:00
unknown
8c17275169 优化项目名称显示。 2024-07-05 22:19:59 +08:00
unknown
6f04f8ccbd 修复在勿扰模式下,使用快捷键启动项目和显示快速搜索窗口仍然生效的问题。 2024-07-05 22:14:56 +08:00
unknown
8c6de3fdc2 更新版本。 2024-06-16 20:21:58 +08:00
unknown
c170df8691 优化主界面搜索、快速搜索项目标题过长导致样式变形的问题。 2024-06-16 20:21:42 +08:00
unknown
2d82702efe 修复分类在顶部,当子分类数量过多超出屏幕范围的问题。 2024-06-16 20:18:54 +08:00
unknown
acb9231915 修复主题非白色的情况下,创建窗口出现闪屏的问题。 2024-06-16 20:15:18 +08:00
unknown
fac3bda8f4 更新版本 2024-05-15 22:46:37 +08:00
unknown
333f0888be 修复获取部分网址信息失败问题 2024-05-15 22:46:09 +08:00
unknown
a94d5a2244 修复锁定尺寸重启软件失效问题 2024-05-15 22:45:07 +08:00
unknown
27a32c7eed 修复在多屏幕情况下显示快速搜索窗口程序崩溃问题 2024-05-15 22:44:14 +08:00
unknown
2cdefb513a update version. 2024-05-01 15:09:55 +08:00
unknown
c9593f379f optimize window display hierarchy issues. 2024-05-01 15:08:31 +08:00
unknown
df499a0517 fix the problem that sometimes the startup fails when booting. 2024-05-01 15:07:09 +08:00
unknown
4048f4a230 recovery item opening logic. 2024-05-01 15:04:29 +08:00
unknown
7c866f57b4 update version. 2024-04-11 19:36:52 +08:00
unknown
e7ce285ae9 fixed an issue that caused the program to crash if the item did not exist when opening the item. 2024-04-11 19:36:37 +08:00
unknown
d117397dc3 update version. 2024-04-10 21:13:12 +08:00
unknown
6b71805274 optimize the opening item logic (solve the problem that some software/files cannot be opened, and too many running programs cause the program to crash). 2024-04-10 21:12:28 +08:00
unknown
480f88d558 fixed the issue where shortcut keys are still occupied after disabling quick search. 2024-04-10 21:06:05 +08:00
unknown
37a62c1cb5 fixed an issue where item opening information would also be copied when copying a item. 2024-04-10 21:04:02 +08:00
unknown
fb0ce7d3ca update version. 2024-03-03 20:12:50 +08:00
unknown
467f33bc99 fixed the bug of invalid environment variables. 2024-03-03 20:11:58 +08:00
unknown
28f3ed000f change version. 2024-02-26 22:19:36 +08:00
unknown
235f964198 quick search add delete history. 2024-02-26 22:16:52 +08:00
unknown
52847f8629 optimize the auto-hide function when docked to the edge of the desktop. 2024-02-26 22:06:52 +08:00
unknown
a11913c785 change version. 2024-01-13 10:36:49 +08:00
unknown
c683d0f167 the quick search window supports multiple screens. 2024-01-13 10:36:33 +08:00
unknown
8a0d1ea08a optimize code. 2024-01-13 10:31:11 +08:00
unknown
d655198b1d settings-Quick search, add dimensions, modify window width. 2024-01-13 10:30:07 +08:00
unknown
607ff84af9 optimize the problem of accidental double-clicking on the taskbar. 2024-01-13 10:23:49 +08:00
unknown
ac9c8c834f optimize the problem of accidental double-clicking on the taskbar. 2024-01-13 10:23:15 +08:00
unknown
af25b93004 optimize the delay issue of clicking the desktop icon to display the main interface. 2024-01-13 09:50:48 +08:00
unknown
90fdc9b8ac shortcut keys support Win key. 2024-01-13 09:49:29 +08:00
unknown
ace6f282d0 fix the abnormal BUG that occurs when using Alt + F4 shortcut keys. 2024-01-13 09:46:06 +08:00
unknown
64007a12e9 stuttering problem occurs when optimizing running projects. 2024-01-13 09:41:04 +08:00
unknown
ee94f20392 fixed the bug where '.' was truncated in the folder project name under the associated folder. 2024-01-13 09:38:44 +08:00
unknown
ca43708a22 fixed a bug that occurred when copying items to a specified classification. 2024-01-13 09:36:54 +08:00
unknown
c73a2b3709 optimize code. 2024-01-13 09:34:41 +08:00
unknown
90594e7f21 optimize style. 2024-01-13 09:29:19 +08:00
unknown
5167e0c084 change readme. 2023-11-28 10:56:58 +08:00
fanchenio
88b5fa8a15 Change version. 2023-11-23 11:11:39 +08:00
fanchenio
da4a19f81a Optimize the overall operation logic of double-clicking the taskbar to reduce accidental touches. 2023-11-23 11:11:21 +08:00
fanchenio
87ab4e3060 Settings-Item-Add the display path function. After it is turned on, the path will be displayed when the mouse is hovering over the project. 2023-11-23 11:06:52 +08:00
fanchenio
e6289d0344 Optimize style. 2023-11-23 10:43:08 +08:00
fanchenio
ffd7c1d8df Optimize style. 2023-11-23 10:40:56 +08:00
fanchenio
f40cab0107 Search, quick search, new command line function, use '> + space' to enter command line mode. 2023-11-23 10:40:04 +08:00
fanchenio
26ae2093e3 Add comment. 2023-11-23 10:02:20 +08:00
fanchenio
dfe16dd36c Remove. 2023-11-23 10:00:26 +08:00
fanchenio
d6894662ea Restore. 2023-11-21 14:23:26 +08:00
unknown
f556343883 Fixed the bug of not exiting batch mode after batch deletion. 2023-11-19 21:24:34 +08:00
unknown
82f5fe8cfe Remove timeout, Remove backgroundThrottling. 2023-11-19 21:21:45 +08:00
unknown
1d1edb95e7 Emoji language. 2023-11-19 21:16:47 +08:00
unknown
da23211600 Settings-System-Added the function of hiding the taskbar, which is enabled by default. 2023-11-19 21:14:14 +08:00
FanChenIO
2bdbdbab16
Merge pull request #13 from WhiteSevs/main
fix bug
2023-11-18 18:41:59 +08:00
白柒
515944539c fix bug
修复开启启动时报错ReferenceError:mainWindow is not defined问题
2023-11-18 16:31:04 +08:00
fanchenio
b982367793 Change License. 2023-11-17 14:36:02 +08:00
fanchenio
426e261397 Merge branch 'main' of github.com:fanchenio/DawnLauncher 2023-11-16 09:38:12 +08:00
fanchenio
3432c7aa6a Language error. 2023-11-16 09:37:54 +08:00
unknown
028845c159 Change version. 2023-11-10 23:00:41 +08:00
unknown
dcb2b1cc38 Settings - Subclassification - Added name alignment function, which can adjust the alignment of subcategory names in the project area on the right side of the main interface. 2023-11-10 22:59:57 +08:00
unknown
0d93c2f922 Optimize the acquisition of APPX application information and obtain more APPX applications. 2023-11-10 22:53:38 +08:00
unknown
946d94cb5b Change README 2023-11-10 22:42:08 +08:00
fanchenio
55e247a5a3 Fixed a bug that caused the software to crash when entering non-English characters when setting shortcut keys. 2023-11-09 09:57:19 +08:00
fanchenio
5173d73b42 Fixed the bug that the quick search window can still be called up by using shortcut keys when quick search is disabled. 2023-11-09 09:55:10 +08:00
fanchenio
e688210a58 Modify Settings-Appearance-Window Corner Angle. 2023-11-09 09:52:27 +08:00
fanchenio
21cddb6d00 Change README 2023-11-08 16:45:35 +08:00
fanchenio
e91dcdfbdb Change version 2023-11-07 10:36:51 +08:00
fanchenio
f2d0a81042 Fixed the issue where the transparent rounded corners of the background failed after setting the background image 2023-11-07 10:36:34 +08:00
fanchenio
187ffeb636 There is a delay problem when optimizing the interface display 2023-11-07 10:34:48 +08:00
fanchenio
f6fe6c9558 Optimization window 2023-11-07 10:33:42 +08:00
fanchenio
80f1c40ad7 Solve transparent window flickering 2023-11-07 10:28:01 +08:00
fanchenio
5d575216a8 Support parsing URL files 2023-11-07 10:27:18 +08:00
unknown
05ef34afd7 resume url search. 2023-11-05 15:37:30 +08:00
fanchenio
d150070212 README 2023-11-03 15:50:29 +08:00
fanchenio
8c5b180b9e README 2023-11-03 15:50:01 +08:00
fanchenio
0b2960d431 README 2023-11-03 15:49:21 +08:00
fanchenio
34e9726228 LICENSE 2023-11-03 15:25:03 +08:00
fanchenio
1eeee5630e change 2023-11-03 15:17:45 +08:00
fanchenio
d448342d13 编译步骤 2023-08-11 13:29:22 +08:00
iamdj
8790c8d629 commit gitignore 2023-07-20 10:05:36 +08:00
188 changed files with 47514 additions and 38468 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 Normal file
View File

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

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' }),
},
)

18
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "windows-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "gcc",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "windows-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

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

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

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

@ -0,0 +1,68 @@
{
"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/**",
"http://127.0.0.1:3344/**"
]
},
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "d:/GITHUB-DAWN-LAUNCHER/DawnLauncher",
"program": "d:/GITHUB-DAWN-LAUNCHER/DawnLauncher/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

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

@ -0,0 +1,70 @@
{
"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"
}
],
"C_Cpp_Runner.msvcBatchPath": "",
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "",
"C_Cpp_Runner.cppStandard": "",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false,
"C_Cpp_Runner.msvcSecureNoWarnings": false
}

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",
]

78
LICENSE
View File

@ -1,65 +1,21 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
MIT License
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright (c) [2023] [Dawn Launcher]
1. Definitions.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright [2023] [Dawn Launcher]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

68
README-ENGLISH.md Normal file
View File

@ -0,0 +1,68 @@
# [简体中文](https://github.com/fanchenio/DawnLauncher) | English
# Dawn Launcher
The `Windows` quick launch tool helps you organize your messy desktop, manage your desktop shortcuts by category, and keep your desktop clean and tidy.
Supports associated folders (real-time synchronization of folder contents), quick search, relative paths (portable paths), scanning the local start menu, locally scanning the local Appx application list, adding URLs and obtaining URL information with one click.
# Technology Stack
`Electron + Vite + Vue3 + TS + Rust`
# Support Platform
`Windows(10/11)`
# Compilation Steps
1. Install `node-gyp`, required to compile SQLite3.
2. Install the `Rust` environment + `Cargo`, which is needed to compile Rust.
3. Then run `yarn install` to install the project dependencies (if you modify the `Rust` code, you need to re-run `yarn install`).
4. `yarn run dev` runs the project locally.
5. `yarn run build` packages the project.
6. The portable version and the installation version need to be packaged twice. By modifying `VITE_INSTALL` in `.env.production`, `true` means the installation version and `false` means the portable version.
# Official Website
[dawnlauncher.com](https://dawnlauncher.com/)
# UI
![UI](/images/soft1.png)
## Subclassification
![Subclassification](/images/soft2.png)
## Custom Theme
![Custom Theme](/images/soft3.png)
## Custom Background
![Custom Background](/images/soft4.png)
## Quick Search
![Quick Search](/images/soft5.png)
## Get URL Information with One Click
![Get URL information with one click](/images/soft6.webp)
## Relative Paths (Portable Paths)
![Relative Paths (Portable Paths)](/images/soft7.png)
## Associate Folders
![Associate Folders](/images/soft8.webp)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/fanchenio/DawnLauncher.svg)](https://starchart.cc/fanchenio/DawnLauncher)
## License
MIT License

View File

@ -1,31 +1,72 @@
# Dawn Launcher
Windows快捷启动工具帮助您整理杂乱无章的桌面分门别类管理您的桌面快捷方式让您的桌面保持干净整洁。
# 简体中文 | [English](https://github.com/fanchenio/DawnLauncher/blob/main/README-ENGLISH.md)
# Dawn Launcher
`Windows`快捷启动工具,帮助您整理杂乱无章的桌面,分门别类管理您的桌面快捷方式,让您的桌面保持干净整洁。
支持关联文件夹(实时同步文件夹内容)、快速搜索、相对路径(便携路径)、扫描本机开始菜单、本地扫描本机 Appx 应用列表、添加网址并一键获取网址信息。
# 技术栈
`Electron + Vite + Vue3 + TS + Rust`
支持相对路径便携路径支持扫描本机开始菜单、本地Appx应用列表添加项目、支持多项目添加一次启动多个项目、支持添加网址并一键获取网址信息。
# 开发框架
Electron
# 支持平台
Windows
`Windows(10/11)`
# 编译步骤
1. 安装`node-gyp`,编译 SQLite3 需要。
2. 安装`Rust`环境 + `Cargo`,编译 Rust 需要。
3. 然后运行`yarn install`安装项目依赖(如果修改了`Rust`代码也需要重新运行`yarn install`)。
4. `yarn run dev`本地运行项目。
5. `yarn run build`打包项目。
6. 便携版和安装版需要分两次打包,通过修改`.env.production`中的`VITE_INSTALL``true`为安装版,`false`为便携版。
# 官网
[dawnlauncher.com](https://dawnlauncher.com/)
# 捐赠
![微信](/images/wechat.png)
![支付宝](/images/alipay.png)
# QQ 群
369652112
# 界面
![界面](/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)
# License
Apache License Version 2.0
![关联文件夹](/images/soft8.webp)
## Stargazers over time
[![Stargazers over time](https://starchart.cc/fanchenio/DawnLauncher.svg)](https://starchart.cc/fanchenio/DawnLauncher)
## License
MIT License

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();
}

42
cargo.toml Normal file
View File

@ -0,0 +1,42 @@
[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",
"Win32_Graphics_Dwm",
"Management_Deployment",
"ApplicationModel",
"Foundation_Collections",
"ApplicationModel_Core",
]
[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;

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

@ -0,0 +1,972 @@
// 简体中文
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: "背景图模式",
backgroundImagePosition: "背景图定位",
backgroundImageTransparency: "背景图透明度",
backup: "备份",
backupRestoreData: "备份/还原数据",
backupRestoreDataPrompt:
"如果要还原 1.2.3 及之前版本的数据,请在还原时选择导入 JSON 格式的文件。",
batchConvertAbsolutePath: "批量转为绝对路径",
batchConvertRelativePath: "批量转为相对路径",
batchCopyTo: "批量复制到",
batchDelete: "批量删除",
batchDeletePrompt: "是否批量删除项目?",
batchMoveTo: "批量移动到",
batchOperation: "批量操作",
batchRefreshIcon: "批量刷新图标",
borderColor: "边框颜色",
bottom: "底部",
byInitialLetter: "按首字母",
byLastOpen: "按最后打开",
byOpenCount: "按打开次数",
calculator: "计算器",
cancel: "取消",
cancelBatchOperation: "取消批量操作",
center: "居中",
checkCode: "校验代码",
checkInvalidItem: "检查无效项目",
checkUpdates: "检查更新",
checkUpdatesPrompt1: '检查到有新版本,点击"确定"跳转官网下载最新版本。',
checkUpdatesPrompt2: "当前已经是最新版本。",
checkUpdatesPrompt3: "检查更新失败,请确认网络。",
classification: "分类",
clear: "清空",
close: "关闭",
colon: "",
colonKeywordSpace: "冒号 + 关键字 + 空格",
color: "颜色",
columnNumber: "列数",
commandLine: "命令行",
commandLinePrompt1: '在"快速搜索"或"搜索"中使用"> + 空格"进入命令行模式。',
commandPrompt: "命令提示符",
computer: "计算机",
computerManagement: "计算机管理",
controlPanel: "控制面板",
convertAbsolutePath: "转为绝对路径",
convertRelativePath: "转为相对路径",
copy: "复制",
copyFullPath: "复制完整路径",
copyTo: "复制到",
createShortcut: "创建快捷方式",
ctrlNumberKey: "Ctrl + 数字键",
cut: "剪切",
default: "默认",
defaultIcon: "默认图标",
delayDisplay: "延迟显示",
delayHide: "延迟隐藏",
delete: "删除",
deleteClassificationPrompt: "是否删除当前分类?",
deleteIcon: "删除图标",
deleteItemPrompt: "是否删除当前项目?",
deleteHistory: "删除历史记录",
description: "描述",
display: "显示",
displayMainInterface: "显示主界面",
documents: "文档",
doubleClickOpen: "双击打开",
doubleClickTaskbar: "双击任务栏",
downloadImagePrompt1: "下载图片失败。",
downloadImagePrompt2: "链接不是图片格式。",
dontPromptAgain: "不再提示",
edgeAutoHide: "停靠在桌面边缘时自动隐藏",
edgeDock: "边缘吸附",
edit: "编辑",
editClassification: "编辑分类",
editItem: "编辑项目",
editSubclassification: "编辑子分类",
emptyRecycleBin: "清空回收站",
enable: "启用",
enableQuickSearch: "启用快速搜索",
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: "隐藏托盘图标",
hideTaskbar: "隐藏任务栏",
hideWindowCollapseSubClassification: "隐藏窗口收起子分类",
hideWindowAfterOpen: "打开后隐藏窗口",
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: "找不到指定的文件夹",
notTransparent: "不透明",
number: "数量",
numberKey: "数字键",
officialWebsite: "官方网站",
ok: "确定",
open: "打开",
openFileLocation: "打开文件所在位置",
openNow: "仅剩一项立即打开",
openCount: "打开次数",
parameters: "参数",
password: "密码",
pasteIcon: "粘贴图标",
paste: "粘贴",
path: "路径",
powerOptions: "电源选项",
powerShell: "PowerShell",
programsFeatures: "程序和功能",
proxy: "代理",
proxyPrompt:
'仅支持 HTTP 代理,填写"地址"时需要带通信协议和端口例如http://127.0.0.1:7890如果没有用户名和密码为空即可。',
prompt: "提示",
quickSearch: "快速搜索",
recordOpenCount: "记录打开次数",
recycleBin: "回收站",
refreshIcon: "刷新图标",
registryEditor: "注册表编辑器",
remark: "备注",
rememberSelectionState: "记住选择状态",
repeat: "重复",
resourceMonitor: "资源监视器",
restart: "重启",
restore: "还原",
restoreDataPrompt: "还原数据失败。",
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:
'与"设置-快速搜索"中的"显示/隐藏"快捷键产生冲突,请重新设置。',
shortcutKeyPrompt7: "非法字符。",
show: "显示",
showFollowMousePosition: "显示时跟随鼠标位置",
showHide: "显示/隐藏",
showOnlyFiles: "只显示文件",
showOnlyFolders: "只显示文件夹",
showPath: "显示路径",
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: "繁体中文",
transparent: "透明",
turnOffMonitor: "关闭显示器",
unlockClassification: "解锁分类",
unlockItem: "解锁项目",
uploadIcon: "上传图标",
url: "网址",
useItemOpen: "从程序外拖动文件到项目图标上用此项目打开文件",
useProxy: "使用代理",
username: "用户名",
update: "更新",
webSearch: "网络搜索",
webSearchModePrompt1:
'输入"冒号 + 关键字 + 空格"或"关键字 + 空格"使用网络搜索,例如使用谷歌搜索,输入":g"或"g",然后按下空格键,进入网络搜索模式。',
webSearchModePrompt2: "{w}为搜索关键字。",
width: "宽度",
window: "窗口",
windowTransparency: "窗口透明度",
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: "背景圖模式",
backgroundImagePosition: "背景圖定位",
backgroundImageTransparency: "背景圖透明度",
backup: "備份",
backupRestoreData: "備份/還原數據",
backupRestoreDataPrompt:
"如果要還原 1.2.3 及之前版本的數據,請在還原時選擇導入 JSON 格式的文件。",
batchConvertAbsolutePath: "批量轉為絕對路徑",
batchConvertRelativePath: "批量轉為相對路徑",
batchCopyTo: "批量復製到",
batchDelete: "批量刪除",
batchDeletePrompt: "是否批量刪除項目?",
batchMoveTo: "批量移動到",
batchOperation: "批量操作",
batchRefreshIcon: "批量刷新圖標",
borderColor: "邊框顏色",
bottom: "底部",
byInitialLetter: "按首字母",
byLastOpen: "按最後打開",
byOpenCount: "按打開次數",
calculator: "計算器",
cancel: "取消",
cancelBatchOperation: "取消批量操作",
center: "居中",
checkCode: "校驗代碼",
checkInvalidItem: "檢查無效項目",
checkUpdates: "檢查更新",
checkUpdatesPrompt1: '檢查到有新版本,點擊"確定"跳轉官網下載最新版本。',
checkUpdatesPrompt2: "當前已經是最新版本。",
checkUpdatesPrompt3: "檢查更新失敗,請確認網絡。",
classification: "分類",
clear: "清空",
close: "關閉",
colon: "",
colonKeywordSpace: "冒號 + 關鍵字 + 空格",
color: "顏色",
columnNumber: "列數",
commandLine: "命令行",
commandLinePrompt1: '在"快速搜索"或"搜索"中使用"> + 空格"進入命令行模式。',
commandPrompt: "命令提示符",
computer: "計算機",
computerManagement: "計算機管理",
controlPanel: "控製面板",
convertAbsolutePath: "轉為絕對路徑",
convertRelativePath: "轉為相對路徑",
copy: "復製",
copyFullPath: "復製完整路徑",
copyTo: "復製到",
createShortcut: "創建快捷方式",
ctrlNumberKey: "Ctrl + 數字鍵",
cut: "剪切",
default: "默認",
defaultIcon: "默認圖標",
delayDisplay: "延遲顯示",
delayHide: "延遲隱藏",
delete: "刪除",
deleteClassificationPrompt: "是否刪除當前分類?",
deleteIcon: "刪除圖標",
deleteItemPrompt: "是否刪除當前項目?",
deleteHistory: "刪除歷史記錄",
description: "描述",
display: "顯示",
displayMainInterface: "顯示主界面",
documents: "文檔",
doubleClickOpen: "雙擊打開",
doubleClickTaskbar: "雙擊任務欄",
downloadImagePrompt1: "下載圖片失敗。",
downloadImagePrompt2: "鏈接不是圖片格式。",
dontPromptAgain: "不再提示",
edgeAutoHide: "停靠在桌面邊緣時自動隱藏",
edgeDock: "邊緣吸附",
edit: "編輯",
editClassification: "編輯分類",
editItem: "編輯項目",
editSubclassification: "編輯子分類",
emptyRecycleBin: "清空回收站",
enable: "啟用",
enableQuickSearch: "啟用快速搜索",
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: "隱藏托盤圖標",
hideTaskbar: "隱藏任務欄",
hideWindowCollapseSubClassification: "隱藏窗口收起子分類",
hideWindowAfterOpen: "打開後隱藏窗口",
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: "找不到指定的文件夾",
notTransparent: "不透明",
number: "數量",
numberKey: "數字鍵",
officialWebsite: "官方網站",
ok: "確定",
open: "打開",
openFileLocation: "打開文件所在位置",
openNow: "僅剩一項立即打開",
openCount: "打開次數",
parameters: "參數",
password: "密碼",
pasteIcon: "粘貼圖標",
paste: "粘貼",
path: "路徑",
powerOptions: "電源選項",
powerShell: "PowerShell",
programsFeatures: "程序和功能",
proxy: "代理",
proxyPrompt:
'僅支持 HTTP 代理,填寫"地址"時需要帶通信協議和端口例如http://127.0.0.1:7890如果沒有用戶名和密碼為空即可。',
prompt: "提示",
quickSearch: "快速搜索",
recordOpenCount: "記錄打開次數",
recycleBin: "回收站",
refreshIcon: "刷新圖標",
registryEditor: "註冊表編輯器",
remark: "備註",
rememberSelectionState: "記住選擇狀態",
repeat: "重復",
resourceMonitor: "資源監視器",
restart: "重啟",
restore: "還原",
restoreDataPrompt: "還原數據失敗。",
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:
'與"設置-快速搜索"中的"顯示/隱藏"快捷鍵產生沖突,請重新設置。',
shortcutKeyPrompt7: "非法字符。",
show: "顯示",
showFollowMousePosition: "顯示時跟隨鼠標位置",
showHide: "顯示/隱藏",
showOnlyFiles: "只顯示文件",
showOnlyFolders: "只顯示文件夾",
showPath: "顯示路徑",
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: "繁體中文",
transparent: "透明",
turnOffMonitor: "關閉顯示器",
unlockClassification: "解鎖分類",
unlockItem: "解鎖項目",
uploadIcon: "上傳圖標",
url: "網址",
useItemOpen: "從程序外拖動文件到項目圖標上用此項目打開文件",
useProxy: "使用代理",
username: "用戶名",
update: "更新",
webSearch: "網絡搜索",
webSearchModePrompt1:
'輸入"冒號 + 關鍵字 + 空格"或"關鍵字 + 空格"使用網絡搜索,例如使用谷歌搜索,輸入":g"或"g",然後按下空格鍵,進入網絡搜索模式。',
webSearchModePrompt2: "{w}為搜索關鍵字。",
width: "寬度",
window: "窗口",
windowTransparency: "窗口透明度",
zoom: "縮放",
};
// 英语
let english = {
about: "About",
add: "Add",
address: "Address",
aggregateClassification: "Aggregate Classification",
aggregateClassificationPrompt1:
"Aggregate classification will combine items from all classifications and display them according to the set sorting order and item count.",
aggregateClassificationPrompt2:
"The current classification is a parent classification and cannot be set as an aggregate classification.",
aggregateClassificationPrompt3:
"The current sorting is set to by open count. You need to enable Settings - Item - Record open count.",
aggregateClassificationPrompt4:
"Setting an aggregate classification will clear all items under the original classification. Please proceed with caution. Are you sure?",
align: "Align",
all: "All",
altNumberKey: "Alt + Number Key",
alwaysCenter: "Always Center",
alwaysTop: "Always on Top",
appearance: "Appearance",
appx: "Appx",
associateFolder: "Associate Folder",
associateFolderPrompt1:
"Associate folder can monitor changes in the specified folder in real-time and synchronize them with the corresponding classification.",
associateFolderPrompt2:
"Name of the file/folder to be hidden, multiple separated by commas",
associateFolderPrompt3:
"The current classification is a parent classification and cannot be set as an associate folder.",
associateFolderPrompt4: "The target path does not exist.",
associateFolderPrompt5: "The destination path must be a folder.",
associateFolderPrompt6:
"Setting an associate folder will clear all items under the original classification. Please proceed with caution. Are you sure?",
associateFolderPrompt7:
"The target path is empty. The current classification will be automatically converted to a regular classification. Are you sure?",
autoSwitchClassification:
"Automatically switch classification when the item list scrolls to the bottom or top",
backgroundIcon: "Background Icon",
backgroundImage: "Background Image",
backgroundImageMode: "Background Image Mode",
backgroundImagePosition: "Background Image Position",
backgroundImageTransparency: "Background Image Transparency",
backup: "Backup",
backupRestoreData: "Backup/Restore Data",
backupRestoreDataPrompt:
"To restore data from version 1.2.3 and earlier, please choose to import a JSON file during the restoration process.",
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",
byOpenCount: "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:
"A new version has been detected. Click 'OK' to go to the official website and download the latest version.",
checkUpdatesPrompt2: "You are already on the latest version.",
checkUpdatesPrompt3:
"Update check failed. Please check your network connection.",
classification: "Classification",
clear: "Clear",
close: "Close",
colon: ": ",
colonKeywordSpace: "Colon + Keyword + Space",
color: "Color",
columnNumber: "Column Number",
commandLine: "Command Line",
commandLinePrompt1:
'Use "> + Space" in "Quick Search" or "Search" to enter command-line mode.',
commandPrompt: "Command Prompt",
computer: "Computer",
computerManagement: "Computer Management",
controlPanel: "Control Panel",
convertAbsolutePath: "Convert to Absolute Path",
convertRelativePath: "Convert to Relative Path",
copy: "Copy",
copyFullPath: "Copy Full Path",
copyTo: "Copy to",
createShortcut: "Create Shortcut",
ctrlNumberKey: "Ctrl + Number Key",
cut: "Cut",
default: "Default",
defaultIcon: "Default Icon",
delayDisplay: "Delay Display",
delayHide: "Delay Hide",
delete: "Delete",
deleteClassificationPrompt: "Delete current classification?",
deleteIcon: "Delete Icon",
deleteItemPrompt: "Delete current item?",
deleteHistory: "Delete History",
description: "Description",
display: "Display",
displayMainInterface: "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: "Automatically hide when docked to the edge of the desktop",
edgeDock: "Edge Dock",
edit: "Edit",
editClassification: "Edit Classification",
editItem: "Edit Item",
editSubclassification: "Edit Subclassification",
emptyRecycleBin: "Empty Recycle Bin",
enable: "Enable",
enableQuickSearch: "Enable Quick Search",
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: "Loss of Focus Hide",
hideName: "Hide Name",
hideTray: "Hide Tray Icon",
hideTaskbar: "Hide Taskbar",
hideWindowCollapseSubClassification:
"Collapse subclassifications when the window is hidden",
hideWindowAfterOpen: "Hide Window After Opening",
history: "History",
hover: "Hover",
icon: "Icon",
imageLink: "Image Link",
item: "Item",
itemAddEditPrompt1:
"After selecting a fixed icon, the current item will not participate in icon refreshing.",
itemAddEditPrompt2: "Leave empty if there are no special requirements",
itemAddEditPrompt3: "Add successfully.",
itemAddEditPrompt4: "The target does not exist.",
itemAddEditPrompt5: "Failed to retrieve website 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 image formats.",
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 enabling Do Not Disturb mode, Dawn Launcher will not pop up during full-screen games or applications.",
notFoundFile: "The file specified cannot be found",
notFoundFolder: "The specified folder cannot be found",
notTransparent: "Not Transparent",
number: "Number",
numberKey: "Number Key",
officialWebsite: "Official Website",
ok: "OK",
open: "Open",
openFileLocation: "Open File Location",
openNow: "Only one item left to open immediately",
openCount: "Open Count",
parameters: "Parameters",
password: "Password",
pasteIcon: "Paste Icon",
paste: "Paste",
path: "Path",
powerOptions: "Power Options",
powerShell: "PowerShell",
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.',
prompt: "Prompt",
quickSearch: "Quick Search",
recordOpenCount: "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.",
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.',
shortcutKeyPrompt7: "Invaild symbol.",
show: "Show",
showFollowMousePosition: "Follow the mouse position when displayed",
showHide: "Show/Hide",
showOnlyFiles: "Show Only Files",
showOnlyFolders: "Show Only Folders",
showPath: "Show Path",
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 SVG code, you need to click the "Validate Code" button first.',
switch: "Switch",
switchClassificationCollapseOtherSubClassification:
"Collapse other subclassifications when switching classifications",
switchEnglish:
"Switch the input method to English mode when the window is displayed",
system: "System",
target: "Target",
taskManager: "Task Manager",
theme: "Theme",
timeInterval: "Time Interval",
tile: "Tile",
title: "Title",
top: "Top",
traditionalChinese: "Traditional Chinese",
transparent: "Transparent",
turnOffMonitor: "Turn Off Monitor",
unlockClassification: "Unlock Classification",
unlockItem: "Unlock Item",
uploadIcon: "Upload Icon",
url: "URL",
useItemOpen:
"Open files with this item when dragging files from outside the program to the item icon",
useProxy: "Use Proxy",
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",
windowTransparency: "Window Transparency",
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: "#505050",
secondFontColor: "#505050",
mainBackgroundColor: "#FFFFFF",
secondBackgroundColor: "#DCDEDF",
borderColor: "#F0F0F0",
},
{
name: "#2B2B2B",
mainFontColor: "#BBBBBB",
secondFontColor: "#BBBBBB",
mainBackgroundColor: "#2B2B2B",
secondBackgroundColor: "#3C3F41",
borderColor: "#373737",
},
{
name: "#508CC8",
mainFontColor: "#FFFFFF",
secondFontColor: "#FFFFFF",
mainBackgroundColor: "#508CC8",
secondBackgroundColor: "#6FA0D2",
borderColor: "#5A94CD",
},
{
name: "#024351",
mainFontColor: "#FFFFFF",
secondFontColor: "#FFFFFF",
mainBackgroundColor: "#024351",
secondBackgroundColor: "#025A6C",
borderColor: "#064D5C",
},
{
name: "#516FA3",
mainFontColor: "#FFFFFF",
secondFontColor: "#FFFFFF",
mainBackgroundColor: "#516FA3",
secondBackgroundColor: "#91A8D0",
borderColor: "#5D78A7",
},
{
name: "#45326E",
mainFontColor: "#FFFFFF",
secondFontColor: "#FFFFFF",
mainBackgroundColor: "#45326E",
secondBackgroundColor: "#5F4B8B",
borderColor: "#4C3A73",
},
{
name: "#693030",
mainFontColor: "#FFFFFF",
secondFontColor: "#FFFFFF",
mainBackgroundColor: "#693030",
secondBackgroundColor: "#955151",
borderColor: "#733535",
},
{
name: "#9F2F4A",
mainFontColor: "#FFFFFF",
secondFontColor: "#FFFFFF",
mainBackgroundColor: "#9F2F4A",
secondBackgroundColor: "#EA6F8C",
borderColor: "#AD3350",
},
{
name: "#000000,#FFDB00",
mainFontColor: "#B3B3B3",
secondFontColor: "#000000",
mainBackgroundColor: "#000000",
secondBackgroundColor: "#FFDB00",
borderColor: "#1D1D1D",
},
{
name: "#000000,#FFFFFF",
mainFontColor: "#B3B3B3",
secondFontColor: "#000000",
mainBackgroundColor: "#000000",
secondBackgroundColor: "#FFFFFF",
borderColor: "#1D1D1D",
},
];
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;

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

@ -0,0 +1,408 @@
import {
Appearance,
Classification,
CommandLine,
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,
hideTaskbar = true,
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;
hideTaskbar?: 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,
hideTaskbar: hideTaskbar ?? true,
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,
itemAreaNameAlign = "left",
}: {
itemAreaNameFontSize?: number | null;
itemAreaNameFontWeight?: number | null;
itemAreaNameFontLineHeight?: number | null;
itemAreaNameAlign?: "left" | "center" | "right" | null;
}): SubClassification {
return {
itemAreaNameFontSize: itemAreaNameFontSize ?? 14,
itemAreaNameFontWeight: itemAreaNameFontWeight ?? 700,
itemAreaNameFontLineHeight: itemAreaNameFontLineHeight ?? 1.25,
itemAreaNameAlign: itemAreaNameAlign ?? "left",
};
}
/**
*
* @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,
promptShowPath = false,
}: {
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;
promptShowPath?: boolean | 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,
promptShowPath: promptShowPath ?? false,
};
}
/**
*
* @returns
*/
function getQuickSearch({
enable = true,
showHideShortcutKey = "Alt + Space",
openShortcutKey = "none",
hideLoseFocus = false,
openNow = false,
showHistory = false,
showHistorySort = "lastOpen",
useItemOpen = false,
openAfterHideQuickSearchWindow = true,
matchConditionsRemark = false,
width = 600,
}: {
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;
width?: number | 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,
width: width ?? 600,
};
}
/**
*
* @returns
*/
function getWebSearch({
mode = 0,
searchSourceList = webSearchSourceList,
}: {
mode?: number | null;
searchSourceList?: Array<WebSearchSource> | null;
}): WebSearch {
return {
mode: mode ?? 0,
searchSourceList: searchSourceList ?? webSearchSourceList,
};
}
/**
*
* @returns
*/
function getCommandLine({
defaultUse = "cmd",
}: {
defaultUse?: string | null;
}): CommandLine {
return {
defaultUse: defaultUse ?? "cmd",
};
}
/**
*
* @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 : {}
),
commandLine: getCommandLine(
setting && setting.commandLine ? setting.commandLine : {}
),
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 };

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

@ -0,0 +1,170 @@
import { resolve, dirname, parse, join } from "node:path";
import { isAbsolutePath } from "../../commons/utils/common";
import mime from "mime";
import { readFileSync } from "node:fs";
import icojs from "icojs";
// 图标格式
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 pathArr: Array<string> = [];
// 判断是否是网络路径,以\\开头
let isNetwork = false;
if (path.indexOf("\\\\") === 0) {
isNetwork = true;
}
// 是否是一级路径
let isBase = false;
if (!parsedPath.dir || parsedPath.dir.trim() === "") {
// 如果为空代表路径只有一级也就是当前文件本身比如C:/1.txt
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);
}
// 拼接
let newPath = join(...newPathArr);
// 如果是网络路径,在最前面加\\
if (isNetwork) {
newPath = "\\\\" + newPath;
}
return newPath;
}
/**
*
* @returns
*/
function parsePath(path: string) {
// 尝试解析环境变量
path = parseEnvPath(path);
// 是否是相对路径
if (!isAbsolutePath(path)) {
return resolve(
process.env.NODE_ENV === "development"
? resolve(".")
: dirname(process.execPath),
path
);
}
return path;
}
/**
*
* @param filePath
*/
async 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);
if (ext === "ico") {
let images = await icojs.parseICO(buffer);
const largestImage = images.reduce((max, current) => {
if (current.width * current.height > max.width * max.height) {
return current;
}
return max;
});
buffer = Buffer.from(largestImage.buffer);
}
icon =
"data:" +
mime.getType(filePath) +
";base64," +
buffer.toString("base64");
} else {
// 获取图标
icon = global.addon.getFileIcon(filePath);
}
}
return icon;
}
export { getURLParams, parsePath, 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,61 @@
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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 tableName = "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 ${tableName} (
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 ${tableName}`;
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 ${tableName}`;
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 ${tableName} (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 ${tableName} 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 ${tableName} 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 ${tableName} 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 ${tableName} 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 ${tableName} 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 ${tableName} 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 ${tableName} 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 ${tableName} 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 ${tableName}`;
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 ${tableName} 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,966 @@
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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.byOpenCount,
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,340 @@
import { Menu, MenuItem, ipcMain } 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,
showMessageBoxSync,
} 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 = showMessageBoxSync(
"mainWindow",
global.language.deleteClassificationPrompt,
"question",
[global.language.ok, global.language.cancel]
);
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,591 @@
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,
Display,
OpenDialogOptions,
SaveDialogOptions,
app,
dialog,
nativeImage,
nativeTheme,
screen,
} 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(),
},
maxRedirects: 5,
jar: true,
},
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(),
},
maxRedirects: 5,
jar: true,
},
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();
if (!result.name || result.name.trim() === "") {
result.name = $("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(),
},
maxRedirects: 5,
jar: true,
},
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 {
if (result.name && result.name.trim() !== "") {
result.status = true;
}
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();
}
}
}
/**
*
*/
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;
}
}
/**
*
*/
function getWindowInScreen(window: BrowserWindow) {
let inDisplays: Array<Display> = [];
let displays = screen.getAllDisplays();
let bounds = window.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;
}
/**
*
*/
function showMessageBoxSync(
windowName: string,
message: string,
type: "error" | "question" | "info",
buttons: Array<string> | null
) {
// 记录状态
if (windowName === "mainWindow") {
global.mainWindowShowDialog = true;
}
let res = dialog.showMessageBoxSync(getWindow(windowName), {
title: "Dawn Launcher",
message: message,
buttons: buttons,
type: type,
noLink: true,
cancelId: 1,
});
// 删除状态
if (windowName === "mainWindow") {
global.mainWindowShowDialog = false;
}
return res;
}
/**
*
*/
function showErrorMessageBox(windowName: string, message: string) {
showMessageBoxSync(windowName, message, "error", [global.language.ok]);
}
/**
*
*/
function showSaveDialogSync(windowName: string, options: SaveDialogOptions) {
// 记录状态
if (windowName === "mainWindow") {
global.mainWindowShowDialog = true;
}
let path = dialog.showSaveDialogSync(getWindow(windowName), options);
// 删除状态
if (windowName === "mainWindow") {
global.mainWindowShowDialog = false;
}
return path;
}
/**
* /
*/
function showOpenDialogSync(windowName: string, options: OpenDialogOptions) {
// 记录状态
if (windowName === "mainWindow") {
global.mainWindowShowDialog = true;
}
let pathList = dialog.showOpenDialogSync(getWindow(windowName), options);
// 删除状态
if (windowName === "mainWindow") {
global.mainWindowShowDialog = false;
}
return pathList;
}
/**
*
*/
function mainWindowExist() {
return global.mainWindow && !global.mainWindow.isDestroyed();
}
export {
downloadImage,
getURLInfo,
convertPath,
sendAllWindows,
closeWindow,
getDot,
getWindow,
sendToWebContent,
closeAllChildProcess,
openAfterHideWindow,
relaunch,
getUserDataPath,
getMainBackgorunColor,
getWindowInScreen,
showMessageBoxSync,
showErrorMessageBox,
showSaveDialogSync,
showOpenDialogSync,
mainWindowExist,
};

View File

@ -0,0 +1,187 @@
import { app, ipcMain, Menu, OpenDialogSyncOptions, shell } from "electron";
import { getFileIcon } from "../../commons/utils";
import mime from "mime";
import { ShortcutInfo } from "../../../types/common";
import {
convertPath,
downloadImage,
getURLInfo,
sendToWebContent,
showErrorMessageBox,
showMessageBoxSync,
showOpenDialogSync,
} from ".";
import { statSync } from "node:fs";
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) => {
showMessageBoxSync(args.windowName, args.message, "info", [
global.language.ok,
]);
});
// 对话框
ipcMain.on("showConfirmBox", (event, args) => {
// 弹出对话框
let res = showMessageBoxSync(args.windowName, args.message, "question", [
global.language.ok,
global.language.cancel,
]);
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 = showOpenDialogSync(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 = showOpenDialogSync(windowName, options);
if (dirPathList && dirPathList.length > 0) {
let dirPath = dirPathList[0];
event.returnValue = dirPath;
} else {
event.returnValue = null;
}
});
// 获取图标
ipcMain.on("getFileIcon", async (event, args) => {
// 窗口名称
let windowName: string = args.windowName;
// 路径
let filePath: string | null = args.path;
if (filePath) {
// 图标
let icon: string | null = await 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();
});
// 运行
ipcMain.on("run", (event, args) => {
global.addon.shellExecute(
args.operation,
args.target,
args.params ?? "",
app.getPath("home")
);
});
// 文本框菜单
ipcMain.on("textRightMenu", (event, args) => {
// 菜单
let menu = Menu.buildFromTemplate([
{
role: "cut",
label: global.language.cut,
},
{
role: "copy",
label: global.language.copy,
},
{
role: "paste",
label: global.language.paste,
},
]);
// 显示
menu.popup();
});
}

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,74 @@
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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,86 @@
import { ipcMain } from "electron";
import { backupData, createBackupRestoreDataWindow } from ".";
import {
closeWindow,
relaunch,
showErrorMessageBox,
showOpenDialogSync,
showSaveDialogSync,
} from "../commons";
import { restore } from "./data";
import { rmSync } from "node:fs";
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 = showSaveDialogSync("backupRestoreDataWindow", {
defaultPath: "Data",
filters: [{ name: "DB", extensions: ["db"] }],
});
if (filePath && filePath.trim() !== "") {
try {
rmSync(filePath);
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
backupData(filePath).finally(() => {
// 关闭备份/恢复窗口
closeWindow(global.backupRestoreDataWindow);
});
}
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
});
// 恢复数据
ipcMain.on("restoreData", () => {
try {
let filePathList = showOpenDialogSync("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
);
}
});
}

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

@ -0,0 +1,190 @@
import { app, BrowserWindow, dialog } from "electron";
import { join, dirname, basename } 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,
update as updateSetting,
} 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"));
app.setPath("sessionData", 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;
// 解决透明窗口闪烁
app.commandLine.appendSwitch("wm-window-animations-disabled");
// 初始化设置数据
settingDataInit();
// 如果主窗口是非透明窗口的话禁止GPU加速解决白屏问题。
// 如果主窗口是透明窗口的话将除主窗口外的窗口都改为透明度0.99,解决解决白屏问题。
if (global.setting.appearance.transparency === 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 {
// 禁止多开
const instanceLock = app.requestSingleInstanceLock();
if (!instanceLock) {
app.quit();
return;
}
// addon
global.addon = require("../../native/addon.node");
if (global.first) {
// 首次添加,判断系统语言
if (app.getLocale().toLowerCase().indexOf("zh-") === 0) {
// 简体中文
global.setting.general.language = "SimplifiedChinese";
} else {
// 英文
global.setting.general.language = "English";
}
// 修改
updateSetting(global.setting);
}
// 获取语言
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({
message: "达咩呦达咩达咩~",
buttons: [global.language.ok],
type: "error",
noLink: true,
});
app.quit();
return;
}
}
// 初始化数据
classificationDataInit();
itemDataInit();
initSystemItem();
// 初始化监听
indexIpcEvent();
commonIpcEvent();
classificationIpcEvent();
itemIpcEvent();
settingIpcEvent();
searchIpcEvent();
aboutIpcEvent();
dataIpcEvent();
// 创建主窗口
createMainWindow();
if (global.setting.quickSearch.enable) {
// 创建快速搜索窗口
createQuickSearchWindow();
}
// 设置快捷键
setShortcutKey();
// 每次开启软件时都设置一次开机启动选项
if (process.env.NODE_ENV !== "development") {
const exeName = basename(process.execPath);
app.setLoginItemSettings({
openAtLogin: global.setting.general.startup,
openAsHidden: false,
path: process.execPath,
args: ["--processStart", `"${exeName}"`],
});
}
} 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 (global.mainWindow) {
if (!global.mainWindow.isVisible()) {
global.mainWindow.show();
global.mainWindow.focus();
global.blurHide = true;
} else {
global.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 };

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

@ -0,0 +1,457 @@
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 tableName = "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 ${tableName} (
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 ${tableName}
(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 ${tableName}
(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 ${tableName}
(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 ${tableName}
(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;
item.classificationId = classificationId;
resultList.push(item);
}
newOrder++;
}
})();
return resultList;
}
/**
*
* @param item
*/
function update(item: Item) {
// SQL
let sql = `UPDATE ${tableName}
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 ${tableName}
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 ${tableName}
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 ${tableName} 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 ${tableName} 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 ${tableName}`;
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 ${tableName} 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 ${tableName} WHERE id = ?`;
// 运行
let res = db.prepare(sql).run(id).changes > 0;
if (res) {
// 更新序号
reorder(item.classificationId);
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
*
*/
function batchDel(idList: Array<number>) {
// 批量查询
let itemList = selectByIdList(true, idList);
// 参数
let params = itemList.map((item) => item.id).join(",");
// SQL
let sql = `DELETE FROM ${tableName} WHERE id in (${params})`;
// 运行
let res = db.prepare(sql).run().changes > 0;
if (res) {
// 提取分类ID
let classificationIdList = new Set<number>();
itemList.forEach((item) => {
classificationIdList.add(item.classificationId);
});
// 更新序号
classificationIdList.forEach((classificationId) => {
reorder(classificationId);
});
return true;
} else {
return false;
}
}
/**
*
* @param classificationId
*/
function deleteByClassificationId(classificationId: number) {
// SQL
let sql = `DELETE FROM ${tableName} 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 ${tableName} 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 ${tableName} 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,
batchDel,
selectById,
selectByIdList,
deleteByClassificationId,
updateOrder,
updateData,
updateClassificationId,
};

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

@ -0,0 +1,809 @@
import { BrowserWindow, shell, app } from "electron";
import { extname, join } from "node:path";
import { parsePath, getURLParams, getFileIcon } from "../../commons/utils";
import { Item } from "../../../types/item";
import {
batchAdd,
deleteByClassificationId,
list,
selectById,
selectByIdList,
updateData,
updateOrder,
} from "./data";
import { writeFile, statSync, readFileSync, accessSync } from "node:fs";
import mime from "mime";
import {
deleteExtname,
getFileName,
getItemName,
isAbsolutePath,
newItem,
} from "../../../commons/utils/common";
import { iconExts } from "../../commons/utils";
import { addAssociateFolderWatcher } from "../classification";
import {
closeWindow,
convertPath,
getMainBackgorunColor,
sendToWebContent,
showMessageBoxSync,
showSaveDialogSync,
} from "../commons/index";
import { fork } from "../../commons/utilityProcessUtils";
import { ShortcutInfo } from "../../../types/common";
// 窗口
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
focusable: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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) {
// 清空打开信息
itemList.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
*/
async function drop(classificationId: number, pathList: Array<string>) {
// 获取项目信息
let resultList = await getDropItemInfo(classificationId, pathList);
// 添加项目
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" | "explore",
item: Item
) {
if (item.data) {
if (operation === "open" && item.data.runAsAdmin) {
operation = "runas";
} else if (operation === "open" && item.type === 1) {
operation = "explore";
}
// 更新打开信息
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 = parsePath(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 + '"';
showMessageBoxSync("mainWindow", message, "error", [
global.language.ok,
]);
}
}
}
}
/**
*
* @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 = showSaveDialogSync("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 = parsePath(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;
}
/**
*
* @param itemId
*/
function deleteQuickSearchHistory(itemId: number) {
// 查询
let item = selectById(itemId);
if (item) {
// 重置历史记录
item.data.quickSearchLastOpen = 0;
item.data.quickSearchOpenNumber = 0;
// 更新
updateData(itemId, item.data);
// 通知主页面
sendToWebContent("mainWindow", "onUpdateOpenInfo", {
id: itemId,
quickSearchOpenNumber: item.data.quickSearchOpenNumber,
quickSearchLastOpen: item.data.quickSearchLastOpen,
type: "quickSearch",
});
// 通知快速搜索页面
sendToWebContent("quickSearchWindow", "onUpdateOpenInfo", {
id: itemId,
quickSearchOpenNumber: item.data.quickSearchOpenNumber,
quickSearchLastOpen: item.data.quickSearchLastOpen,
});
}
}
/**
*
* @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;
}
}
}
// 获取图标
item.data.icon = await getFileIcon(item.data.target);
// 获取后缀判断是否是url
let ext = extname(item.data.target);
if (ext && ext.toLowerCase() === ".url") {
// url
let url = parseUrlFileContent(readFileSync(item.data.target, "utf-8"));
if (url && url.trim() !== "") {
item.data.target = url;
item.type = 2;
} else {
continue;
}
} else {
// 文件类型
let stats = statSync(item.data.target);
item.type = stats.isFile() ? 0 : 1;
}
// 去掉后缀
if (item.type === 0 || item.type === 2) {
item.name = deleteExtname(item.name);
}
// push
itemList.push(item);
} catch (e) {}
}
return itemList;
}
/**
* .url文件内容以获取URL
* @param content
* @returns
*/
function parseUrlFileContent(content: string) {
if (content) {
const lines = content.split("\n");
for (const line of lines) {
if (line.startsWith("URL=")) {
const url = line.substring(4).trim();
return url;
}
}
}
return null;
}
export {
createAddEditWindow,
createNetworkIconWindow,
createSVGIconWindow,
copy,
move,
drop,
updateOpenInfo,
run,
convertTarget,
exportIcon,
refreshIcon,
createShortcut,
getDirectoryList,
getClipboardImageFile,
pasteIcon,
checkInvalid,
deleteQuickSearchHistory,
};

View File

@ -0,0 +1,720 @@
import {
Menu,
MenuItem,
ipcMain,
clipboard,
MenuItemConstructorOptions,
} from "electron";
import { parsePath } from "../../commons/utils";
import {
convertTarget,
createAddEditWindow,
createNetworkIconWindow,
createSVGIconWindow,
createShortcut,
drop,
exportIcon,
refreshIcon,
run,
copy,
move,
getClipboardImageFile,
pasteIcon,
updateOpenInfo,
deleteQuickSearchHistory,
} from ".";
import {
getAppxItemList,
getStartMenuItemList,
getSystemItemList,
} from "./commons";
import {
add,
batchDel,
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,
showMessageBoxSync,
} 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 point = global.addon.getCursorPoint();
// 菜单
let menuList: Array<MenuItem> = [];
// 组装菜单
if (!batchOperation) {
if (item) {
// 查询页面分类信息
let pageClassification = selectClassificationById(
args.pageClassificationId
);
if (!pageClassification && type === "main") {
return;
}
// 后缀
let ext: string | null = null;
// 排除
if (item.type === 0) {
ext = getFileExtname(item.data.target);
}
// "打开"菜单
let openMenu = false;
if (
item.type === 0 ||
item.type === 4 ||
(item.type === 3 && item.data.target === "cmd.exe")
) {
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 display = getWindowInScreen(
type === "main" ? global.mainWindow : global.quickSearchWindow
);
let scaleFactor = 1;
if (display && display.length > 0) {
scaleFactor = display[0].scaleFactor;
}
// 弹出资源管理器菜单
sendToWebContent(
type === "quickSearch" ? "quickSearchWindow" : "mainWindow",
"onItemExplorerMenu",
{
type: type,
id: item.id,
}
);
// 禁用鼠标HOOK
global.addon.disableMouseHook();
// 弹出资源管理器菜单
global.addon.explorerContextMenu(
(type === "main"
? global.mainWindow
: global.quickSearchWindow
)
.getNativeWindowHandle()
.readInt32LE(0),
item.type === 0 || item.type === 1
? parsePath(item.data.target)
: item.data.target,
point[0],
point[1]
);
// 开启鼠标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
? parsePath(item.data.target)
: item.data.target
);
},
})
);
pathMenu = true;
}
if (
type === "main" &&
(item.type === 0 || item.type === 1) &&
classification.type === 0 &&
pageClassification.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) {
menuList.push(
new MenuItem({
label: global.language.createShortcut,
click: () => {
createShortcut(item);
},
})
);
pathMenu = true;
}
if (pathMenu) {
menuList.push(new MenuItem({ type: "separator" }));
}
if (
type === "main" &&
classification.type === 0 &&
pageClassification.type === 0
) {
// "图标"菜单
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 &&
classification.type === 0 &&
pageClassification.type === 0
) {
menuList.push(
new MenuItem({
label: global.language.refreshIcon,
click: () => {
refreshIcon([item.id]);
},
})
);
}
if (
type === "main" &&
classification.type === 0 &&
pageClassification.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 = showMessageBoxSync(
"mainWindow",
global.language.deleteItemPrompt,
"question",
[global.language.ok, global.language.cancel]
);
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 = showMessageBoxSync(
"mainWindow",
global.language.batchDeletePrompt,
"question",
[global.language.ok, global.language.cancel]
);
if (res === 0) {
// 批量删除
batchDel(batchSelectedIdList);
// 快捷键
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: parsePath(item.data.target),
icon: icon,
});
} finally {
// 取消拖出状态
sendToWebContent("mainWindow", "onItemCancelDragOut", {});
}
});
// 更新打开信息
ipcMain.on("updateItemOpenInfo", (event, args) => {
updateOpenInfo(args.type, args.id);
});
// 删除历史记录
ipcMain.on("deleteQuickSearchHistory", (event, args) => {
deleteQuickSearchHistory(args);
});
}

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

@ -0,0 +1,840 @@
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,
getWindowInScreen,
mainWindowExist,
sendToWebContent,
} from "../commons";
import { release } from "node:os";
// 窗口
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,
frame: false,
show: false,
maximizable: false,
minimizable: false,
fullscreenable: false,
transparent: global.setting.appearance.transparency < 1,
skipTaskbar: global.setting.general.hideTaskbar,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
icon: join(process.env.VITE_PUBLIC, "logo.ico"),
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", () => {
// 设置窗口无动画
global.addon.removeWindowAnimation(
mainWindow.getNativeWindowHandle().readInt32LE(0)
);
// 永远居中不可移动
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");
}
// 恢复上一次的位置
let bounds: any = cacheData.cacheStore.get("mainWindowBounds");
// 锁定尺寸
mainWindow.setResizable(!global.setting.general.lockSize);
// 如果是锁定尺寸的话,使用锁定尺寸来设置窗口尺寸
if (global.setting.general.lockSize) {
let lockSizeBounds: any = cacheData.cacheStore.get(
"mainWindowLockSizeBounds"
);
if (lockSizeBounds) {
if (bounds) {
bounds.width = lockSizeBounds.width;
bounds.height = lockSizeBounds.height;
} else {
bounds = {
width: lockSizeBounds.width,
height: lockSizeBounds.height,
};
}
} else {
if (bounds) {
cacheData.cacheStore.set("mainWindowLockSizeBounds", bounds);
} else {
cacheData.cacheStore.set(
"mainWindowLockSizeBounds",
mainWindow.getBounds()
);
}
}
}
if (bounds) {
mainWindow.setBounds(bounds);
}
// 托盘
createTray(!global.setting.general.hideTray);
// 永远居中
alwaysCenter();
// 判断窗口位置
let displays = getWindowInScreen(mainWindow);
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) {
autoHide(0, false);
}
if (
mainWindow.isVisible() &&
global.setting.general.hideLoseFocus &&
!global.setting.general.alwaysTop
) {
// 如果当前还有打开的子窗口就不执行失去焦点隐藏
if (
mainWindow.getChildWindows().length === 0 &&
!global.mainWindowShowDialog
) {
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", {});
}
});
// 主窗口关闭事件
mainWindow.on("closed", () => {
app.quit();
});
// 创建鼠标hook
let mousedownClassName = null;
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;
let className: string =
!res.class_name || res.class_name.trim() === ""
? null
: res.class_name.trim();
// 1左键 2右键 3滚轮
let button: number = res.button;
if (event === "mousemove") {
// 鼠标移动
if (!global.blurHide) {
// 停靠在桌面边缘时自动隐藏
autoHide(20, true);
}
} else if (event === "mousedown") {
// 鼠标按下
if (button === 1) {
mousedownClassName = className;
}
} else if (event === "mouseup") {
// 鼠标抬起
if (button === 1) {
// 双击任务栏
doubleClickTaskbar(mousedownClassName, className);
} else if (button === 3) {
// 中间单击
showHideMouseWheelClick();
}
// 失去焦点隐藏
onBlurHide();
}
});
// 禁用标题栏右键
mainWindow.hookWindowMessage(278, function (e) {
// 窗口禁用
mainWindow.setEnabled(false);
// 延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
setTimeout(() => {
mainWindow.setEnabled(true);
}, 100);
return true;
});
}
/**
*
*/
function onBlurHide() {
if (
mainWindowExist() &&
global.mainWindow.isVisible() &&
global.setting.general.hideLoseFocus &&
!global.setting.general.alwaysTop &&
global.mainWindow.getChildWindows().length === 0 &&
!global.mainWindowShowDialog &&
!hasCursorPosWindow(global.mainWindow)
) {
// 隐藏窗口
hideMainWindow();
}
}
/**
*
*/
function hasCursorPosWindow(window: BrowserWindow) {
if (window && !window.isDestroyed() && window.isVisible()) {
// 获取鼠标位置
let point = screen.getCursorScreenPoint();
// 窗口位置信息
let bounds = window.getBounds();
// 判断鼠标是否在窗口以外
if (
point.x < bounds.x ||
point.x > bounds.x + bounds.width ||
point.y < bounds.y ||
point.y > bounds.y + bounds.height
) {
// 窗口以外
return false;
} else {
// 窗口以内
return true;
}
}
return false;
}
/**
*
* @param blurHide
* @param autoHide
* @param selectedClassificationId
*/
function showMainWindowBefore(
blurHide: boolean,
autoHide = false,
selectedClassificationId: number | null = null
) {
// 向主窗口发送通知
sendToWebContent("mainWindow", "onShowMainWindowBefore", {
blurHide,
autoHide,
selectedClassificationId,
});
}
/**
*
* @param blurHide
* @param autoHide
*/
function showMainWindow(blurHide: boolean, autoHide = false) {
if (!mainWindowExist()) {
return;
}
// flag
let flag = true;
// 是否开启勿扰模式
if (global.setting.general.notDisturb) {
if (global.addon.isFullscreen()) {
flag = false;
}
}
if (!autoHide) {
// 显示时跟随鼠标位置
showFollowMousePosition();
}
if (flag) {
if (!global.setting.general.alwaysTop) {
global.mainWindow.setAlwaysOnTop(true, "screen-saver");
}
global.mainWindow.show();
global.mainWindow.focus();
if (!global.setting.general.alwaysTop) {
global.mainWindow.setAlwaysOnTop(false);
}
global.blurHide = blurHide;
}
}
/**
*
*/
function hideMainWindow() {
if (mainWindowExist() && 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;
}
}
}
/**
*
* @param display
* @param workArea
* @returns
*/
function edgeAdsorb(display: Display | null, workArea = false) {
if (!mainWindowExist()) {
return;
}
// 如果勾选停靠在桌面边缘时自动隐藏,放行
if (
!global.setting.general.edgeAdsorb &&
!global.setting.general.edgeAutoHide
) {
return;
}
try {
// 清空方向
global.mainWindowDirection = null;
// 屏幕
let displays = display ? [display] : getWindowInScreen(global.mainWindow);
if (displays.length > 1 || displays.length === 0) {
return;
}
// 屏幕区域
let displayBounds = workArea ? displays[0].workArea : displays[0].bounds;
// 窗口位置信息
let bounds = global.mainWindow.getBounds();
if (bounds.x <= displayBounds.x && bounds.y <= displayBounds.y) {
// 左上角
global.mainWindow.setBounds({ x: displayBounds.x, y: displayBounds.y });
global.mainWindowDirection = "leftTop";
global.blurHide = false;
} else if (
bounds.x + bounds.width >= displayBounds.x + displayBounds.width &&
bounds.y <= displayBounds.y
) {
// 右上角
global.mainWindow.setBounds({
x: displayBounds.x + displayBounds.width - bounds.width,
y: displayBounds.y,
});
global.mainWindowDirection = "rightTop";
global.blurHide = false;
} else if (
bounds.x <= displayBounds.x &&
bounds.y + bounds.height >= displayBounds.y + displayBounds.height
) {
// 左下角
global.mainWindow.setBounds({
x: displayBounds.x,
y: displayBounds.y + displayBounds.height - bounds.height,
});
global.mainWindowDirection = "leftBottom";
global.blurHide = false;
} else if (
bounds.x + bounds.width >= displayBounds.x + displayBounds.width &&
bounds.y + bounds.height >= displayBounds.y + displayBounds.height
) {
// 右下角
global.mainWindow.setBounds({
x: displayBounds.x + displayBounds.width - bounds.width,
y: displayBounds.y + displayBounds.height - bounds.height,
});
global.mainWindowDirection = "rightBottom";
global.blurHide = false;
} else if (bounds.x <= displayBounds.x) {
// 左侧
global.mainWindow.setBounds({ x: displayBounds.x });
global.mainWindowDirection = "left";
global.blurHide = false;
} else if (
bounds.x + bounds.width >=
displayBounds.x + displayBounds.width
) {
// 右侧
global.mainWindow.setBounds({
x: displayBounds.x + displayBounds.width - bounds.width,
});
global.mainWindowDirection = "right";
global.blurHide = false;
} else if (
bounds.y + bounds.height >=
displayBounds.y + displayBounds.height
) {
// 底部
global.mainWindow.setBounds({
y: displayBounds.y + displayBounds.height - bounds.height,
});
global.mainWindowDirection = "bottom";
global.blurHide = false;
} else if (bounds.y <= displayBounds.y) {
// 顶部
global.mainWindow.setBounds({ y: displayBounds.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 (!mainWindowExist()) {
return;
}
// 当永远居中、固定位置勾选后不能使用显示时跟随鼠标位置
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);
// 调整窗口位置并显示时会出现闪屏的情况
// https://github.com/electron/electron/issues/10862
for (let i = 0; i < 10; i++) {
global.mainWindow.setSize(bounds.width, bounds.height);
}
// 获取当前鼠标所在屏幕
let display = screen.getDisplayNearestPoint(point);
// 边缘吸附
edgeAdsorb(display, true);
}
}
/**
* /
*/
function showHideMouseWheelClick() {
if (!mainWindowExist()) {
return;
}
if (global.setting.general.showHideMouseWheelClick) {
if (global.mainWindow.isVisible()) {
hideMainWindow();
} else {
showMainWindowBefore(true);
}
}
}
/**
*
*/
function alwaysCenter() {
if (!mainWindowExist()) {
return;
}
if (global.setting.general.alwaysCenter) {
global.mainWindow.center();
}
}
/**
*
* @param size
* @param timer /
* @returns
*/
function autoHide(size: number, timer: boolean) {
if (!mainWindowExist()) {
return;
}
if (!global.setting.general.edgeAutoHide) {
return;
}
// 当有子窗口时不自动隐藏
if (
global.mainWindow.getChildWindows().length > 0 ||
global.mainWindowShowDialog
) {
return;
}
let x = screen.getCursorScreenPoint().x;
let y = screen.getCursorScreenPoint().y;
try {
// 屏幕
let displays = getWindowInScreen(global.mainWindow);
if (displays.length > 1 || displays.length === 0) {
return;
}
// 屏幕区域
let displayBounds = displays[0].bounds;
// 窗口位置信息
let bounds = global.mainWindow.getBounds();
if (global.mainWindow.isVisible()) {
let flag = false;
if (bounds.x === displayBounds.x && bounds.y === displayBounds.y) {
// 左上角
flag =
x >= bounds.x + bounds.width + size ||
y >= bounds.y + bounds.height + size;
} else if (
bounds.x + bounds.width === displayBounds.x + displayBounds.width &&
bounds.y === displayBounds.y
) {
// 右上角
flag = x <= bounds.x - size || y >= bounds.y + bounds.height + size;
} else if (
bounds.x === displayBounds.x &&
bounds.y + bounds.height === displayBounds.y + displayBounds.height
) {
// 左下角
flag = x >= bounds.x + bounds.width + size || y <= bounds.y - size;
} else if (
bounds.x + bounds.width === displayBounds.x + displayBounds.width &&
bounds.y + bounds.height === displayBounds.y + displayBounds.height
) {
// 右下角
flag = x <= bounds.x - size || y <= bounds.y - size;
} else if (
bounds.x + bounds.width >=
displayBounds.x + displayBounds.width
) {
// 右侧
flag =
x <= bounds.x - size ||
y <= bounds.y - size ||
y >= bounds.y + bounds.height + size;
} else if (bounds.x === displayBounds.x) {
// 左侧
flag =
x > bounds.x + bounds.width + size ||
y <= bounds.y - size ||
y >= bounds.y + bounds.height + size;
} else if (
bounds.y + bounds.height >=
displayBounds.y + displayBounds.height
) {
// 底部
flag =
y < bounds.y - size ||
x <= bounds.x - size ||
x >= bounds.x + bounds.width + size;
} else if (bounds.y === displayBounds.y) {
// 顶部
flag =
y > bounds.y + bounds.height + size ||
x <= bounds.x - size ||
x >= bounds.x + bounds.width + 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;
if (
global.mainWindowDirection === "leftTop" &&
x === displayBounds.x &&
y === displayBounds.y
) {
// 左上角
flag = true;
} else if (
global.mainWindowDirection === "rightTop" &&
x === displayBounds.x + displayBounds.width - 1 &&
y === displayBounds.y
) {
// 右上角
flag = true;
} else if (
global.mainWindowDirection === "leftBottom" &&
x === displayBounds.x &&
y === displayBounds.y + displayBounds.height - 1
) {
// 左下角
flag = true;
} else if (
global.mainWindowDirection === "rightBottom" &&
x === displayBounds.x + displayBounds.width - 1 &&
y === displayBounds.y + displayBounds.height - 1
) {
// 右下角
flag = true;
} else if (
global.mainWindowDirection === "left" &&
x <= displayBounds.x &&
y >= bounds.y &&
y <= bounds.y + bounds.height
) {
// 左侧
flag = true;
} else if (
global.mainWindowDirection === "right" &&
x >= bounds.x + bounds.width - 1 &&
y >= bounds.y &&
y <= bounds.y + bounds.height
) {
// 右侧
flag = true;
} else if (
global.mainWindowDirection === "bottom" &&
y >= bounds.y + bounds.height - 1 &&
x >= bounds.x &&
x <= bounds.x + bounds.width
) {
// 底部
flag = true;
} else if (
global.mainWindowDirection === "top" &&
y <= displayBounds.y &&
x >= bounds.x &&
x <= bounds.x + bounds.width
) {
// 顶部
flag = true;
}
if (flag) {
if (
timer &&
global.setting.general.delayDisplayMs > 0 &&
!global.autoHideTimer
) {
// 延迟显示
global.autoHideTimer = setTimeout(function () {
showMainWindowBefore(false, true);
}, global.setting.general.delayDisplayMs);
} else if (!timer || global.setting.general.delayDisplayMs === 0) {
// 显示
showMainWindowBefore(false, true);
}
} 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(
mousedownClassName: string | null,
className: string | null
) {
if (!mainWindowExist()) {
return;
}
// 必须开启设置
if (!global.setting.general.showHideDoubleClickTaskbar) {
return;
}
// 获取屏幕
let displays = getWindowInScreen(global.mainWindow);
if (
displays.length > 1 ||
displays.length === 0 ||
(className !== "Shell_TrayWnd" && className !== "Shell_SecondaryTrayWnd")
) {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
return;
}
// 必须是指定Class
if (
(release().startsWith("10.0.1") &&
global.addon.getCursorPosWindowClassName().indexOf("MSTask") >= 0) ||
(release().startsWith("10.0.2") &&
global.addon.getCursorPosWindowClassName() !== "TrayNotifyWnd")
) {
// 监听双击
if (!global.doubleClickTaskbarCounter) {
global.doubleClickTaskbarCounter = 0;
}
// +1
global.doubleClickTaskbarCounter++;
if (
global.doubleClickTaskbarCounter &&
global.doubleClickTaskbarCounter === 2 &&
(mousedownClassName === "Shell_TrayWnd" ||
mousedownClassName === "Shell_SecondaryTrayWnd")
) {
// 清除timeout
clearTimeout(global.doubleClickTaskbarTimer);
// 清空计数
global.doubleClickTaskbarCounter = 0;
// 显示或隐藏
if (global.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;
}
}
export {
createMainWindow,
showMainWindowBefore,
showMainWindow,
hideMainWindow,
createTray,
edgeAdsorb,
getWindowInScreen,
showHideMouseWheelClick,
alwaysCenter,
autoHide,
};

View File

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

View File

@ -0,0 +1,175 @@
import { BrowserWindow, shell, screen } from "electron";
import { join } from "node:path";
import {
getMainBackgorunColor,
getWindowInScreen,
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: global.setting.quickSearch.width,
height: 44,
type: "toolbar",
frame: false,
show: false,
maximizable: false,
minimizable: false,
fullscreenable: false,
resizable: false,
alwaysOnTop: true,
transparent: global.setting.appearance.transparency < 1,
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) {
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() {
// flag
let flag = true;
// 是否开启勿扰模式
if (global.setting.general.notDisturb) {
if (global.addon.isFullscreen()) {
flag = false;
}
}
if (flag) {
// 获取鼠标所在的屏幕
let currentDisplay = screen.getDisplayNearestPoint(
screen.getCursorScreenPoint()
);
// 获取窗口所在的屏幕
let windowDisplay = getWindowInScreen(quickSearchWindow);
if (windowDisplay.length === 0) {
// 代表窗口的位置不再任一屏幕内,将窗口位置移动到主窗口
quickSearchWindow.center();
} else if (
(windowDisplay.length === 1 &&
currentDisplay.id !== windowDisplay[0].id) ||
windowDisplay.length > 1
) {
// 在鼠标所在的屏幕显示
let workArea = currentDisplay.workArea;
let bounds = quickSearchWindow.getBounds();
let x = Math.round(workArea.x + workArea.width / 2 - bounds.width / 2);
let y = Math.round(workArea.y + workArea.height / 2 - 44 / 2);
quickSearchWindow.setPosition(x, y);
for (let i = 0; i < 10; i++) {
quickSearchWindow.setSize(global.setting.quickSearch.width, 44);
}
}
// 显示
quickSearchWindow.setBounds({ width: global.setting.quickSearch.width });
quickSearchWindow.show();
}
}
/**
*
*/
function hideQuickSearchWindow() {
quickSearchWindow.hide();
}
export {
createQuickSearchWindow,
showQuickSearchWindow,
hideQuickSearchWindow,
showQuickSearchWindowBefore,
};

View File

@ -0,0 +1,25 @@
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 });
});
// 设置快速搜索窗口宽度
ipcMain.on("setQuickSearchWindowWidth", (event, args) => {
global.quickSearchWindow.setBounds({ width: args });
});
}

View File

@ -0,0 +1,85 @@
import { Setting } from "../../../types/setting";
import { getSetting } from "../../../commons/utils/setting";
import { getDataSqlite3 } from "../../commons/betterSqlite3";
import { app } from "electron";
// 获取数据库
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;
// 添加设置被视为首次打开软件
global.first = true;
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,211 @@
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,
mainWindowExist,
} 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,
maximizable: false,
minimizable: false,
resizable: false,
fullscreenable: false,
skipTaskbar: true,
show: false,
transparent: global.setting.appearance.transparency < 1,
backgroundColor:
global.setting.appearance.transparency === 1
? getMainBackgorunColor()
: null,
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() !== ""
) {
try {
globalShortcut.register(
setting.general.showHideShortcutKey.replace("Win", "Super"),
() => {
if (mainWindowExist()) {
if (global.mainWindow.isVisible()) {
hideMainWindow();
} else {
showMainWindowBefore(true);
}
}
}
);
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
}
// 分类快捷键
let classificationList = selectClassificationList();
for (const classification of classificationList) {
if (classification.globalShortcutKey && classification.shortcutKey) {
try {
globalShortcut.register(
classification.shortcutKey.replace("Win", "Super"),
() => {
if (mainWindowExist()) {
// 分类
showMainWindowBefore(true, false, classification.id);
}
}
);
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
}
}
// 项目快捷键
let itemList = selectItemList();
for (const item of itemList) {
if (item.globalShortcutKey && item.shortcutKey) {
try {
globalShortcut.register(
item.shortcutKey.replace("Win", "Super"),
() => {
if (mainWindowExist()) {
// flag
let flag = true;
// 是否开启勿扰模式
if (global.setting.general.notDisturb) {
if (global.addon.isFullscreen()) {
flag = false;
}
}
if (flag) {
// 项目
run("main", "open", item);
}
}
}
);
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
}
}
// 快速搜索
if (
setting.quickSearch.enable &&
setting.quickSearch.showHideShortcutKey &&
setting.quickSearch.showHideShortcutKey.trim() !== ""
) {
try {
globalShortcut.register(
setting.quickSearch.showHideShortcutKey.replace("Win", "Super"),
() => {
if (global.setting.quickSearch.enable) {
// 如果窗口不存在或者被销毁的话,就创建窗口
if (
!global.quickSearchWindow ||
global.quickSearchWindow.isDestroyed()
) {
createQuickSearchWindow();
}
// 如果初始化完毕并且窗口状态是正常的话,可以进行显示/隐藏
if (
global.quickSearchWindowInit &&
global.quickSearchWindow &&
!global.quickSearchWindow.isDestroyed()
) {
if (global.quickSearchWindow.isVisible()) {
hideQuickSearchWindow();
} else {
showQuickSearchWindowBefore();
}
}
}
}
);
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.log(e);
}
}
}
}
/**
*
* @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,216 @@
import { app, ipcMain } from "electron";
import {
closeWindow,
getUserDataPath,
relaunch,
sendToWebContent,
showOpenDialogSync,
} 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";
import cacheData from "../commons/cacheData";
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);
if (args) {
// 存储主窗口尺寸
cacheData.cacheStore.set(
"mainWindowLockSizeBounds",
global.mainWindow.getBounds()
);
}
});
// 固定位置
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 = showOpenDialogSync("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();
}
});
}

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

@ -0,0 +1,675 @@
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 xml2js from "xml2js";
import { newCommonItem, newCommonItemData } from "../../commons/utils/common";
import { ShortcutInfo } from "../../types/common";
import { parsePath, getFileIcon } from "../commons/utils";
// AppxInfo
export interface AppxInfo {
id: 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 === "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: await getFileIcon(filePath),
params,
}),
});
// push
resultList.push(item);
}
}
}
}
return resultList;
}
/**
* APPX项目
*/
async function getAppxItemList() {
// 返回列表
let resultList: Array<CommonItem> = [];
try {
// ID
let id = 1;
// 获取APPX信息
let appxList = global.addon.getAppxList();
// 临时列表
let tempList = [];
// 读取XML获取图标路径和名称
for (let appx of appxList) {
tempList.push(...(await getAppxInfo(appx)));
}
// 图标转BASE64
for (let appxInfo of tempList) {
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;
}
}
// 筛选出有图标的数据
tempList = tempList.filter((e) => e.icon);
// 返回列表
for (const appxInfo of tempList) {
resultList.push(
newCommonItem({
id: id++,
name: appxInfo.name,
data: newCommonItemData({
icon: appxInfo.icon,
target: "Shell:AppsFolder\\" + appxInfo.id,
}),
})
);
}
// 排序
resultList.sort((a, b) => a.name.localeCompare(b.name));
} catch (e) {}
return resultList;
}
/**
* Appx信息
*/
async function getAppxInfo(appx: any) {
// 结果列表
let resultList = [];
// buffer, 解析结果
let buffer: Buffer, result: any;
try {
// 解析
buffer = readFileSync(join(appx.path, "AppxManifest.xml"));
result = await xml2jsSync(buffer);
// 循环Application
if (
result.Package.Applications &&
result.Package.Applications[0] &&
result.Package.Applications[0].Application &&
result.Package.Applications[0].Application.length > 0
) {
for (
let i = 0;
i < result.Package.Applications[0].Application.length;
i++
) {
// appx信息
let appxInfo: AppxInfo = {
id: null,
icon: null,
name: null,
};
// Application
const application = result.Package.Applications[0].Application[i];
// 名称
if (appx["appName" + i] && appx["appName" + i].trim() !== "") {
appxInfo.name = appx["appName" + i];
} else {
appxInfo.name = appx.displayName;
}
// 获取ID
let id = application.$.Id;
if (!id || id.trim() === "") {
continue;
}
appxInfo.id = appx.familyName + "!" + id;
// 图标
try {
// targetsize图标
let targetSizeIcon: string | null = null;
let targetSizeIconMax: number | null = null;
// scale图标
let scaleIcon = null;
let scaleIconMax = null;
// 图标
if (application["uap:VisualElements"]) {
// logo地址
let logo = application["uap:VisualElements"][0].$.Square44x44Logo;
// 解析路径
let parsedPath = parse(logo);
// 获取文件夹下所有文件
let fileNameList = readdirSync(join(appx.path, 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
? appx.path +
"\\" +
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 =
appx.path +
"\\" +
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 =
appx.path +
"\\" +
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 =
appx.path +
"\\" +
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 =
appx.path + "\\" + 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 =
appx.path +
"\\" +
parsedPath.dir +
"\\" +
defaultList[0];
}
}
}
}
} else {
if (filterList.length === 1) {
// 只有一张图片
appxInfo.icon =
appx.path + "\\" + 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(appx.path, result);
if (propertiesIcon) {
appxInfo.icon = propertiesIcon;
}
}
}
} catch (e) {
if (result) {
let propertiesIcon = getPropertiesIcon(appx.path, result);
if (propertiesIcon) {
appxInfo.icon = propertiesIcon;
}
}
}
if (
(!appxInfo.icon || appxInfo.icon.trim() === "") &&
appx.logo &&
appx.logo.trim() !== ""
) {
appxInfo.icon = appx.logo;
}
if (
appxInfo.name &&
appxInfo.name.trim() !== "" &&
appxInfo.id &&
appxInfo.id.trim() !== "" &&
appxInfo.icon &&
appxInfo.icon.trim() !== ""
) {
resultList.push(appxInfo);
}
}
}
} catch (ex) {}
return resultList;
}
/**
* 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 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 = await 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 =
type === 0 ? deleteExtname(getFileName(path)) : getFileName(path);
item.data.target = path;
item.data.icon = await 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 = parsePath(item.data.target);
try {
statSync(path);
} catch (e) {
resultList.push(item.id);
}
}
}
return resultList;
}

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

@ -0,0 +1,875 @@
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");
},
// 运行
run: (
operation: string,
target: string,
params: string | null,
startLocation: string | null
) => {
ipcRenderer.send("run", { operation, target, params, startLocation });
},
// 文本框菜单
textRightMenu: () => {
ipcRenderer.send("textRightMenu");
},
});
contextBridge.exposeInMainWorld("main", {
// 显示窗口
showWindow: (blurHide: boolean, autoHide: boolean) => {
ipcRenderer.send("showMainWindow", { blurHide, autoHide });
},
// 隐藏窗口
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 });
},
// 删除历史记录
deleteQuickSearchHistory: (id: number) => {
ipcRenderer.send("deleteQuickSearchHistory", 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);
},
// 隐藏任务栏
setTaskbar: (show: boolean) => {
ipcRenderer.send("setTaskbar", 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);
},
// 设置窗口宽度
setWindowWidth: (width: number) => {
ipcRenderer.send("setQuickSearchWindowWidth", width);
},
});
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");
},
});

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

@ -0,0 +1,79 @@
import { BrowserWindow, Tray } from "electron";
import { FSWatcher } from "node:fs";
import { Setting } from "../../types/setting";
declare global {
// addon
var addon: any;
// 是否是首次打开软件
var first: boolean;
// 语言
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;
// 存储主窗口当前是否有弹出对话框
var mainWindowShowDialog: boolean;
}
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,75 @@
{
"name": "DawnLauncher",
"name": "dawn-launcher",
"productName": "Dawn Launcher",
"author": "Dawn Launcher",
"version": "1.2.3",
"version": "1.5.1",
"main": "dist-electron/main/index.js",
"description": "Windows 快捷启动工具,帮助您整理杂乱无章的桌面,分门别类管理您的桌面快捷方式,让您的桌面保持干净整洁。",
"author": "FanChenIO",
"private": true,
"gypfile": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
"keywords": [
"electron",
"vite",
"vue"
],
"debug": {
"env": {
"VITE_DEV_SERVER_URL": "http://127.0.0.1:3344/"
}
},
"dependencies": {
"@ckpack/vue-color": "^1.4.1",
"bindings": "^1.5.0",
"cheerio": "^1.0.0-rc.12",
"core-js": "^3.8.3",
"dompurify": "^3.0.3",
"electron-store": "^8.1.0",
"jimp": "^0.22.7",
"mime": "^3.0.0",
"node-addon-api": "^5.0.0",
"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"
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build && electron-builder",
"preview": "vite preview",
"rsbuild": "napi build --release --strip ./native",
"rebuild": "electron rebuild.js",
"postinstall": "yarn run rebuild && yarn run rsbuild"
},
"napi": {
"name": "addon"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@tailwindcss/forms": "^0.5.3",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"autoprefixer": "^10.4.8",
"electron": "^22.3.17",
"electron-builder": "^24.4.0",
"electron-devtools-installer": "^3.2.0",
"modclean": "^3.0.0-beta.1",
"postcss": "^8.4.16",
"prettier": "^2.7.1",
"tailwindcss": "^3.1.8",
"terser-webpack-plugin": "^5.3.8",
"vue-cli-plugin-electron-builder": "^3.0.0-alpha.4"
"@napi-rs/cli": "^2.16.3",
"@vicons/ionicons5": "^0.12.0",
"@vicons/material": "^0.12.0",
"@vicons/utils": "^0.1.4",
"@vitejs/plugin-vue": "^4.4.0",
"autoprefixer": "^10.4.16",
"better-sqlite3-multiple-ciphers": "^9.4.1",
"electron": "^28.3.1",
"electron-builder": "^24.6.5",
"less": "^4.2.0",
"less-loader": "^11.1.3",
"naive-ui": "^2.36.0",
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"typescript": "^5.2.2",
"vite": "^4.4.11",
"vite-plugin-electron": "^0.15.4",
"vue": "^3.4.13",
"vue-tsc": "^1.8.22"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
"dependencies": {
"@types/dompurify": "^3.0.3",
"@types/mime": "^3.0.2",
"@types/request": "^2.48.10",
"@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",
"icojs": "^0.19.3",
"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

@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {},
autoprefixer: {},
},
}
}

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);
});

203
rust/lib.rs Normal file
View File

@ -0,0 +1,203 @@
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 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]
fn get_cursor_pos_window_class_name() -> String {
windows::get_cursor_pos_window_class_name()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn get_clipboard_file_list() -> Vec<String> {
windows::get_clipboard_file_list()
}
/**
* BITMAP
*/
#[allow(dead_code)]
#[napi]
fn clipboard_has_bitmap() -> bool {
windows::clipboard_has_bitmap()
}
/**
* BITMAP的BASE64
*/
#[allow(dead_code)]
#[napi]
fn get_clipboard_bitmap_base64() -> Option<String> {
windows::get_clipboard_bitmap_base64()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn empty_recycle_bin(window: i32) {
windows::empty_recycle_bin(window)
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn remove_window_animation(window: i32) {
windows::remove_window_animation(window);
}
/**
* APPX列表
*/
#[allow(dead_code)]
#[napi]
fn get_appx_list() -> Vec<HashMap<String, String>> {
windows::get_appx_list()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
fn get_cursor_point() -> [i32; 2] {
windows::get_cursor_point()
}
/**
*
*/
#[allow(dead_code)]
#[napi]
pub fn turn_off_monitor() {
windows::turn_off_monitor()
}
/**
*
*/
#[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())
}

806
rust/windows.rs Normal file
View File

@ -0,0 +1,806 @@
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::thread;
use std::{
collections::HashMap,
io::Cursor,
path::Path,
process::Command,
sync::atomic::{AtomicBool, Ordering},
};
use windows::{
core::{ComInterface, HSTRING, PCSTR, PCWSTR, PSTR},
w,
Win32::{
Foundation::{HWND, LPARAM, LRESULT, MAX_PATH, POINT, RECT, SIZE, WPARAM},
Graphics::{
Dwm::{DwmSetWindowAttribute, DWMWA_TRANSITIONS_FORCEDISABLED},
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,
},
UI::{
Input::Ime::{
ImmGetContext, ImmReleaseContext, ImmSetConversionStatus, IME_CMODE_ALPHANUMERIC,
IME_SMODE_AUTOMATIC,
},
Shell::{
BHID_SFUIObject, IContextMenu, IShellItem, IShellItemImageFactory, IShellLinkW,
SHCreateItemFromParsingName, SHEmptyRecycleBinW, SHQueryUserNotificationState,
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, 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,
},
},
},
};
use windows::{
Management::Deployment::PackageManager,
Win32::{
System::SystemInformation::GetSystemDirectoryW,
UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_SHOWDEFAULT},
},
};
// 获取图标并转为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 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 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,
class_name: String,
}
/**
* 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 mut class_name = String::new();
// 参数
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");
class_name.push_str(&get_foreground_window_class_name());
} else if param == WM_LBUTTONDOWN || param == WM_RBUTTONDOWN || param == WM_MBUTTONDOWN
{
event.push_str("mousedown");
class_name.push_str(&get_foreground_window_class_name());
} else if param == WM_MOUSEWHEEL || param == WM_MOUSEHWHEEL {
event.push_str("mousewheel");
class_name.push_str(&get_foreground_window_class_name());
}
// 按键类型
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,
class_name,
};
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);
};
}
/**
*
*/
pub fn remove_window_animation(window: i32) {
// HWND
let hwnd = HWND(window as isize);
let pvattribute = &mut true as *mut _ as *const _;
unsafe {
let _ = DwmSetWindowAttribute(
hwnd,
DWMWA_TRANSITIONS_FORCEDISABLED,
pvattribute,
std::mem::size_of_val(&pvattribute) as u32,
);
};
}
/**
* APPX列表
*/
pub fn get_appx_list() -> Vec<HashMap<String, String>> {
let mut result_list = vec![];
let package_manager: Result<PackageManager, windows::core::Error> = PackageManager::new();
if package_manager.is_err() {
return result_list;
}
let packages = package_manager
.unwrap()
.FindPackagesByUserSecurityId(&HSTRING::default());
if packages.is_err() {
return result_list;
}
for package in packages.unwrap() {
let mut map = HashMap::new();
if let Ok(diaplay_name) = package.DisplayName() {
map.insert(String::from("displayName"), diaplay_name.to_string());
}
if let Ok(path) = package.InstalledPath() {
map.insert(String::from("path"), path.to_string());
}
if let Ok(id) = package.Id() {
if let Ok(family_name) = id.FamilyName() {
map.insert(String::from("familyName"), family_name.to_string());
}
}
if let Ok(logo) = package.Logo() {
if let Ok(path) = logo.Path() {
map.insert(String::from("logo"), path.to_string());
}
}
if let Ok(app_list) = package.GetAppListEntriesAsync() {
if let Ok(app_list) = app_list.get() {
for (index, app) in app_list.into_iter().enumerate() {
if app.DisplayInfo().is_err()
|| app.DisplayInfo().unwrap().DisplayName().is_err()
{
continue;
}
map.insert(
format!("appName{}", index),
app.DisplayInfo()
.unwrap()
.DisplayName()
.unwrap()
.to_string(),
);
}
}
}
result_list.push(map);
}
result_list
}
/**
*
*/
fn get_foreground_window_class_name() -> String {
let hwnd = unsafe { GetForegroundWindow() };
// 获取窗口的ClassName
let mut buffer = [0u16; MAX_PATH as usize];
unsafe {
GetClassNameW(hwnd, &mut buffer);
};
// 返回
u16_to_string(&buffer)
}
/**
*
*/
pub fn get_cursor_point() -> [i32; 2] {
let mut point = POINT::default();
unsafe {
GetCursorPos(&mut point);
};
[point.x, point.y]
}
/**
*
*/
pub fn shell_execute(
operation: String,
file: String,
params: String,
start_location: Option<String>,
) {
thread::spawn(move || {
// 是否是打开文件夹
let is_dir = operation == "explore";
// 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()
}
});
// 文件夹
let dir_param = format!("\"{}\"", file.to_string());
let dir_param = HSTRING::from(dir_param.as_str());
let dir_param = PCWSTR(dir_param.as_ptr());
// 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 {
if is_dir {
ShellExecuteW(
None,
w!("open"),
w!("explorer.exe"),
dir_param,
None,
SW_SHOWDEFAULT,
);
} else {
ShellExecuteW(None, operation, file, params, dir, SW_SHOWDEFAULT);
}
}
});
}
/**
*
*/
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,
);
}
}
}

352
src/App.vue Normal file
View File

@ -0,0 +1,352 @@
<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: hexToRGBA(store.setting.appearance.theme.mainBackgroundColor, 1),
textColor: hexToRGBA(store.setting.appearance.theme.mainFontColor, 1),
border:
"1px solid " + hexToRGBA(store.setting.appearance.theme.borderColor, 1),
borderChecked:
"1px solid " +
hexToRGBA(store.setting.appearance.theme.mainFontColor, 1),
colorChecked: "none",
checkMarkColor: hexToRGBA(
store.setting.appearance.theme.mainFontColor,
1
),
},
Radio: {
boxShadowActive:
"inset 0 0 0 1px " +
hexToRGBA(store.setting.appearance.theme.mainFontColor, 1),
boxShadowFocus:
"inset 0 0 0 1px " +
hexToRGBA(store.setting.appearance.theme.mainFontColor, 1),
boxShadowHover:
"inset 0 0 0 1px " +
hexToRGBA(store.setting.appearance.theme.mainFontColor, 1),
dotColorActive: hexToRGBA(
store.setting.appearance.theme.mainFontColor,
1
),
color: hexToRGBA(store.setting.appearance.theme.mainBackgroundColor, 1),
textColor: hexToRGBA(store.setting.appearance.theme.mainFontColor, 1),
},
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 styleElement = document.getElementById("placeholder-style");
// style
if (styleElement && styleElement.parentNode) {
styleElement.parentNode.removeChild(styleElement);
}
//
let style = document.createElement("style");
style.setAttribute("id", "placeholder-style");
style.type = "text/css";
//
style.innerHTML =
"input::placeholder, textarea::placeholder {" +
"color: " +
hexToRGBA(store.setting.appearance.theme.mainFontColor, 0.5) +
";" +
"}";
// head
document.head.appendChild(style);
// 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" ||
shortcutKey.toLowerCase() === "alt + f4"
) {
e.preventDefault();
}
}
}
}
//
function contextmenu(e: MouseEvent) {
let target = e.target as HTMLInputElement;
if (target) {
if (
(target.nodeName != null &&
target.nodeName.toLowerCase() == "input" &&
target.type != null &&
target.type.toLowerCase() == "text") ||
(target.nodeName != null && target.nodeName.toLowerCase() == "textarea")
) {
window.api.textRightMenu();
e.preventDefault();
e.stopPropagation();
return;
}
}
}
//
let onUpdateSettingUnListen: Function | null = null;
// mounted
onMounted(() => {
//
createStyle();
//
window.addEventListener("keydown", keydown, true);
//
window.addEventListener("contextmenu", contextmenu, true);
//
onUpdateSettingUnListen = window.setting.onUpdate((data) => {
store.setting = data;
});
});
// unmounted
onUnmounted(() => {
//
window.removeEventListener("keydown", keydown, true);
//
window.removeEventListener("contextmenu", contextmenu, 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

View File

@ -0,0 +1,20 @@
<template>
<span
class="ml-auto text-[12px] py-[4px] px-[6px]"
style="border-radius: 4px"
:style="{
color: store.setting.appearance.theme.mainFontColor,
backgroundColor: store.setting.appearance.theme.secondBackgroundColor,
}"
>{{ text }}</span
>
</template>
<script setup lang="ts">
import { useMainStore } from "../store";
// pinia
const store = useMainStore();
// props
const props = defineProps<{
text: string;
}>();
</script>

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

@ -0,0 +1,195 @@
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;
run: (
operation: string,
target: string,
params: string | null,
startLocation: string | null
) => void;
textRightMenu: () => void;
};
main: {
showWindow: (blurHide: boolean, autoHide: 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;
deleteQuickSearchHistory: (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;
setTaskbar: (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;
setWindowWidth: (width: 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);
});
}

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