This commit is contained in:
2025-12-05 19:00:25 +08:00
parent db3334ec6c
commit fc032b6040
10 changed files with 142 additions and 154 deletions

2
.npmrc Normal file
View File

@@ -0,0 +1,2 @@
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

View File

@@ -1,4 +1,3 @@
// @ts-check
import { resolvePath } from '@kevisual/use-config'; import { resolvePath } from '@kevisual/use-config';
import { execSync } from 'node:child_process'; import { execSync } from 'node:child_process';
@@ -8,6 +7,17 @@ const external = ['pm2'];
/** /**
* @type {import('bun').BuildConfig} * @type {import('bun').BuildConfig}
*/ */
// await Bun.build({
// target: 'node',
// format: 'esm',
// entrypoints: [resolvePath(entry, { meta: import.meta })],
// outdir: resolvePath('./dist', { meta: import.meta }),
// naming: {
// entry: `${naming}.js`,
// },
// external,
// });
await Bun.build({ await Bun.build({
target: 'node', target: 'node',
format: 'esm', format: 'esm',
@@ -16,6 +26,5 @@ await Bun.build({
naming: { naming: {
entry: `${naming}.js`, entry: `${naming}.js`,
}, },
external, external
env: 'KEVISUAL_*',
}); });

3
mod.ts Normal file
View File

@@ -0,0 +1,3 @@
import { Hotkeys } from './src/hot-api/hotkeys/index';
export { Hotkeys }

View File

@@ -1,40 +1,37 @@
{ {
"name": "@kevisual/router-template-server", "name": "@kevisual/hot-api",
"version": "0.0.1", "version": "0.0.1",
"description": "", "description": "",
"main": "index.js", "main": "mod.ts",
"basename": "/root/router-template-server", "basename": "/root/hot-api",
"app": { "app": {
"type": "system-app", "type": "system-app",
"key": "router-template-server",
"entry": "app.js", "entry": "app.js",
"runtime": [ "runtime": [
"server" "client"
] ]
}, },
"scripts": { "scripts": {
"dev": "bun --watch src/main.ts ", "dev": "bun --watch src/main.ts ",
"build": "pnpm run clean && bun run bun.config.mjs", "build": "pnpm run clean && bun run bun.config.ts",
"postbuild": "ev pack", "postbuild": "ev pack",
"compile": "bun build --compile ./src/main.ts --outfile router-template",
"compile:win": "bun build --compile ./src/main.ts --target=bun-windows-x64 --outfile router-template.exe",
"clean": "rm -rf dist && rimraf pack-dist", "clean": "rm -rf dist && rimraf pack-dist",
"pub": "envision pack -p -u" "pub": "envision pack -p -u"
}, },
"files": [ "files": [
"dist" "dist",
"src"
], ],
"keywords": [], "keywords": [],
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)", "author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
"license": "MIT", "license": "MIT",
"packageManager": "pnpm@10.22.0", "packageManager": "pnpm@10.24.0",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@kevisual/ai": "^0.0.11", "@kevisual/ai": "^0.0.16",
"@kevisual/local-proxy": "^0.0.8", "@kevisual/query": "^0.0.30",
"@kevisual/query": "^0.0.29",
"@kevisual/router": "0.0.33", "@kevisual/router": "0.0.33",
"@kevisual/use-config": "^1.0.19", "@kevisual/use-config": "^1.0.21",
"@nut-tree/nut-js": "^4.2.0", "@nut-tree/nut-js": "^4.2.0",
"archiver": "^7.0.1", "archiver": "^7.0.1",
"dayjs": "^1.11.19", "dayjs": "^1.11.19",

88
pnpm-lock.yaml generated
View File

@@ -9,20 +9,17 @@ importers:
.: .:
dependencies: dependencies:
'@kevisual/ai': '@kevisual/ai':
specifier: ^0.0.11 specifier: ^0.0.16
version: 0.0.11 version: 0.0.16
'@kevisual/local-proxy':
specifier: ^0.0.8
version: 0.0.8
'@kevisual/query': '@kevisual/query':
specifier: ^0.0.29 specifier: ^0.0.30
version: 0.0.29 version: 0.0.30
'@kevisual/router': '@kevisual/router':
specifier: 0.0.33 specifier: 0.0.33
version: 0.0.33 version: 0.0.33
'@kevisual/use-config': '@kevisual/use-config':
specifier: ^1.0.19 specifier: ^1.0.21
version: 1.0.19(dotenv@16.6.1) version: 1.0.21(dotenv@16.6.1)
'@nut-tree/nut-js': '@nut-tree/nut-js':
specifier: ^4.2.0 specifier: ^4.2.0
version: 4.2.0 version: 4.2.0
@@ -223,20 +220,20 @@ packages:
'@jimp/utils@0.22.12': '@jimp/utils@0.22.12':
resolution: {integrity: sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==} resolution: {integrity: sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==}
'@kevisual/ai@0.0.11': '@kevisual/ai@0.0.16':
resolution: {integrity: sha512-SceIxodtjttLurZuXImZmJbimSt/NJW7N8vQArr0AaxqUam/WwcOTPYSVCleGhUW196Vew/FKC5k0vjyBMZZ5Q==} resolution: {integrity: sha512-K5KYm+dwHCnB61BhVFh9UcWiOS/FeS29ijvgwE/cQR8RonfPtX/oI7WhAu0jCGGSxTI6cel2LjrpU4JoVzWgnA==}
'@kevisual/load@0.0.6': '@kevisual/load@0.0.6':
resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==} resolution: {integrity: sha512-+3YTFehRcZ1haGel5DKYMUwmi5i6f2psyaPZlfkKU/cOXgkpwoG9/BEqPCnPjicKqqnksEpixVRkyHJ+5bjLVA==}
'@kevisual/local-proxy@0.0.8':
resolution: {integrity: sha512-VX/P+6/Cc8ruqp34ag6gVX073BchUmf5VNZcTV/6MJtjrNE76G8V6TLpBE8bywLnrqyRtFLIspk4QlH8up9B5Q==}
'@kevisual/logger@0.0.4': '@kevisual/logger@0.0.4':
resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==} resolution: {integrity: sha512-+fpr92eokSxoGOW1SIRl/27lPuO+zyY+feR5o2Q4YCNlAdt2x64NwC/w8r/3NEC5QenLgd4K0azyKTI2mHbARw==}
'@kevisual/query@0.0.29': '@kevisual/permission@0.0.3':
resolution: {integrity: sha512-rQZk0J073UuC1QGzuyq+pb4Y0hu8/Qx/xYHs9NbsmslM+RuMnd1zpXmvhXNj7Kn1MdYTH90ng2MlFLBkkQFaIg==} resolution: {integrity: sha512-8JsA/5O5Ax/z+M+MYpFYdlioHE6jNmWMuFSokBWYs9CCAHNiSKMR01YLkoVDoPvncfH/Y8F5K/IEXRCbptuMNA==}
'@kevisual/query@0.0.30':
resolution: {integrity: sha512-mDPEaLX9LdTRgi9anmWQ4EJ491umsASu/gs6K85J5nJqtUN/kfnZ3x5IouUr6aNbgAhrNLv/vTqpQTBsQhEYHQ==}
'@kevisual/router@0.0.33': '@kevisual/router@0.0.33':
resolution: {integrity: sha512-9z7TkSzCIGbXn9SuHPBdZpGwHlAuwA8iN5jNAZBUvbEvBRkBxlrbdCSe9fBYiAHueLm2AceFNrW74uulOiAkqA==} resolution: {integrity: sha512-9z7TkSzCIGbXn9SuHPBdZpGwHlAuwA8iN5jNAZBUvbEvBRkBxlrbdCSe9fBYiAHueLm2AceFNrW74uulOiAkqA==}
@@ -244,10 +241,10 @@ packages:
'@kevisual/types@0.0.10': '@kevisual/types@0.0.10':
resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==} resolution: {integrity: sha512-Q73uzzjk9UidumnmCvOpgzqDDvQxsblz22bIFuoiioUFJWwaparx8bpd8ArRyFojicYL1YJoFDzDZ9j9NN8grA==}
'@kevisual/use-config@1.0.19': '@kevisual/use-config@1.0.21':
resolution: {integrity: sha512-Q1IH4eMqUe5w6Bq8etoqOSls9FPIy0xwwD3wHf26EsQLZadhccI9qkDuFzP/rFWDa57mwFPEfwbGE5UlqWOCkw==} resolution: {integrity: sha512-czgy4+tBDBJI6QTnKh2PCwswET6ZpZ4ZqBE/SPkkOivEtlrcPzLs5elwMLZ3goD1XMD4VB3yjumb5WuW/8H8MA==}
peerDependencies: peerDependencies:
dotenv: ^16.4.7 dotenv: ^17
'@nut-tree/default-clipboard-provider@4.2.0': '@nut-tree/default-clipboard-provider@4.2.0':
resolution: {integrity: sha512-O9EXd7F+iuHcm1XaIT1wNa34WYQDxriKYwUhyofvJpl0fJ0ou3u+nxHITmsr1zoBoERaOhG+PT3MSSNsQ+rTug==} resolution: {integrity: sha512-O9EXd7F+iuHcm1XaIT1wNa34WYQDxriKYwUhyofvJpl0fJ0ou3u+nxHITmsr1zoBoERaOhG+PT3MSSNsQ+rTug==}
@@ -278,10 +275,6 @@ packages:
peerDependencies: peerDependencies:
'@nut-tree/nut-js': ^3 '@nut-tree/nut-js': ^3
'@nut-tree/node-mac-permissions@2.2.1':
resolution: {integrity: sha512-yoijwCqt2yS9UFTv7THGUwZgy2D2TorN703QpYNOergbTJ6vEmeG379pIZDfBuvf56n24RMzKJVoSqr9WIq/Yw==}
os: [darwin]
'@nut-tree/nut-js@4.2.0': '@nut-tree/nut-js@4.2.0':
resolution: {integrity: sha512-/xXsE7Z6+lOiltZgNWy6uPgiReSabgEYiTJFG2VqA9kRELtoiTyLM8ySds78zpPdTYYaL/Usi05+u54pIXnqQA==} resolution: {integrity: sha512-/xXsE7Z6+lOiltZgNWy6uPgiReSabgEYiTJFG2VqA9kRELtoiTyLM8ySds78zpPdTYYaL/Usi05+u54pIXnqQA==}
engines: {node: '>=16'} engines: {node: '>=16'}
@@ -666,9 +659,6 @@ packages:
node-abort-controller@3.1.1: node-abort-controller@3.1.1:
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
node-addon-api@5.0.0:
resolution: {integrity: sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==}
node-fetch@2.7.0: node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0} engines: {node: 4.x || >=6.0.0}
@@ -700,18 +690,6 @@ packages:
once@1.4.0: once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
openai@5.23.2:
resolution: {integrity: sha512-MQBzmTulj+MM5O8SKEk/gL8a7s5mktS9zUtAkU257WjvobGc9nKcBuVwjyEEcb9SI8a8Y2G/mzn3vm9n1Jlleg==}
hasBin: true
peerDependencies:
ws: ^8.18.0
zod: ^3.23.8
peerDependenciesMeta:
ws:
optional: true
zod:
optional: true
p-finally@1.0.0: p-finally@1.0.0:
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -1198,24 +1176,21 @@ snapshots:
dependencies: dependencies:
regenerator-runtime: 0.13.11 regenerator-runtime: 0.13.11
'@kevisual/ai@0.0.11': '@kevisual/ai@0.0.16':
dependencies: dependencies:
'@kevisual/logger': 0.0.4 '@kevisual/logger': 0.0.4
'@kevisual/permission': 0.0.3
'@kevisual/query': 0.0.30
'@kevisual/load@0.0.6': '@kevisual/load@0.0.6':
dependencies: dependencies:
eventemitter3: 5.0.1 eventemitter3: 5.0.1
'@kevisual/local-proxy@0.0.8': {}
'@kevisual/logger@0.0.4': {} '@kevisual/logger@0.0.4': {}
'@kevisual/query@0.0.29': '@kevisual/permission@0.0.3': {}
dependencies:
openai: 5.23.2 '@kevisual/query@0.0.30': {}
transitivePeerDependencies:
- ws
- zod
'@kevisual/router@0.0.33': '@kevisual/router@0.0.33':
dependencies: dependencies:
@@ -1227,7 +1202,7 @@ snapshots:
'@kevisual/types@0.0.10': {} '@kevisual/types@0.0.10': {}
'@kevisual/use-config@1.0.19(dotenv@16.6.1)': '@kevisual/use-config@1.0.21(dotenv@16.6.1)':
dependencies: dependencies:
'@kevisual/load': 0.0.6 '@kevisual/load': 0.0.6
dotenv: 16.6.1 dotenv: 16.6.1
@@ -1240,20 +1215,14 @@ snapshots:
'@nut-tree/libnut-darwin@2.7.1': '@nut-tree/libnut-darwin@2.7.1':
dependencies: dependencies:
bindings: 1.5.0 bindings: 1.5.0
optionalDependencies:
'@nut-tree/node-mac-permissions': 2.2.1
'@nut-tree/libnut-linux@2.7.1': '@nut-tree/libnut-linux@2.7.1':
dependencies: dependencies:
bindings: 1.5.0 bindings: 1.5.0
optionalDependencies:
'@nut-tree/node-mac-permissions': 2.2.1
'@nut-tree/libnut-win32@2.7.1': '@nut-tree/libnut-win32@2.7.1':
dependencies: dependencies:
bindings: 1.5.0 bindings: 1.5.0
optionalDependencies:
'@nut-tree/node-mac-permissions': 2.2.1
'@nut-tree/libnut@4.2.0(@nut-tree/nut-js@4.2.0)': '@nut-tree/libnut@4.2.0(@nut-tree/nut-js@4.2.0)':
dependencies: dependencies:
@@ -1262,12 +1231,6 @@ snapshots:
'@nut-tree/libnut-win32': 2.7.1 '@nut-tree/libnut-win32': 2.7.1
'@nut-tree/nut-js': 4.2.0 '@nut-tree/nut-js': 4.2.0
'@nut-tree/node-mac-permissions@2.2.1':
dependencies:
bindings: 1.5.0
node-addon-api: 5.0.0
optional: true
'@nut-tree/nut-js@4.2.0': '@nut-tree/nut-js@4.2.0':
dependencies: dependencies:
'@nut-tree/default-clipboard-provider': 4.2.0(@nut-tree/nut-js@4.2.0) '@nut-tree/default-clipboard-provider': 4.2.0(@nut-tree/nut-js@4.2.0)
@@ -1656,9 +1619,6 @@ snapshots:
node-abort-controller@3.1.1: {} node-abort-controller@3.1.1: {}
node-addon-api@5.0.0:
optional: true
node-fetch@2.7.0: node-fetch@2.7.0:
dependencies: dependencies:
whatwg-url: 5.0.0 whatwg-url: 5.0.0
@@ -1681,8 +1641,6 @@ snapshots:
dependencies: dependencies:
wrappy: 1.0.2 wrappy: 1.0.2
openai@5.23.2: {}
p-finally@1.0.0: {} p-finally@1.0.0: {}
package-json-from-dist@1.0.1: {} package-json-from-dist@1.0.1: {}

View File

@@ -0,0 +1,89 @@
import { keyboard, Key } from "@nut-tree/nut-js";
/**
* 控制功能部分的案件映射
*/
export const keyMap: Record<string, Key> = {
'ctrl': Key.LeftControl,
'leftctrl': Key.LeftControl,
'rightctrl': Key.RightControl,
'alt': Key.LeftAlt,
'leftalt': Key.LeftAlt,
'rightalt': Key.RightAlt,
'shift': Key.LeftShift,
'leftshift': Key.LeftShift,
'rightshift': Key.RightShift,
'meta': Key.LeftSuper,
'cmd': Key.LeftCmd,
'win': Key.LeftWin,
// 根据操作系统选择 Ctrl 或 Command 键
'ctrlorcommand': process.platform === 'darwin' ? Key.LeftCmd : Key.LeftControl,
};
/**
* 将快捷键字符串转换为 Key 枚举值
* @param hotkey
* @returns
*/
export const parseHotkey = (hotkey: string): Key[] => {
return hotkey
.toLowerCase()
.split('+')
.map(key => {
const trimmed = key.trim().toLowerCase();
// 如果是修饰键,从映射表中获取
if (keyMap[trimmed]) {
return keyMap[trimmed];
}
// 如果是字母,转换为大写并查找对应的 Key
if (trimmed.length === 1 && /[a-z]/.test(trimmed)) {
const upperKey = trimmed.toUpperCase();
return Key[upperKey as keyof typeof Key] as Key;
}
// 其他情况直接查找
return Key[trimmed as keyof typeof Key] as Key;
})
.filter((key): key is Key => key !== undefined);
}
type PressHostKeysOptions = {
hotkey: string;
durationMs?: number;
}
export const pressHotkey = async (opts: PressHostKeysOptions): Promise<boolean> => {
const { hotkey, durationMs = 100 } = opts;
const keys = parseHotkey(hotkey);
console.log('准备模拟按下快捷键:', hotkey);
// 同时按下所有键
await keyboard.pressKey(...keys);
// 短暂延迟后释放
await new Promise(resolve => setTimeout(resolve, durationMs));
// 释放所有键
await keyboard.releaseKey(...keys);
return true
}
/**
* 模拟按下一组快捷键,支持逗号分隔的多个快捷键
* @param opts
* @returns
*/
export const pressHotkeys = async (opts: PressHostKeysOptions): Promise<boolean> => {
let { hotkey } = opts;
hotkey = hotkey.replace(/\s+/g, ''); // 去除所有空格
const hotkeyList = hotkey.split(',').map(hk => hk.trim());
if (hotkeyList.length === 0) {
return await pressHotkey({ ...opts, hotkey });
}
for (const hk of hotkeyList) {
await pressHotkey({ ...opts, hotkey: hk });
// 每个快捷键之间稍作延迟
await new Promise(resolve => setTimeout(resolve, 200));
}
return true;
}
export class Hotkeys {
pressHotkey = pressHotkey;
pressHotkeys = pressHotkeys;
}

View File

@@ -1,4 +1,4 @@
import { app } from './app.ts' import { app } from './app.ts'
import './router/index.ts'; import './routes/index.ts';
export { app } export { app }

View File

@@ -1,17 +1,9 @@
import { app } from './app.ts' import { app } from './app.ts'
import './router/index.ts'; import './routes/index.ts';
import { HOME } from './module/config.ts'; import { HOME } from './module/config.ts';
import { proxyRoute, initProxy } from '@kevisual/local-proxy/proxy.ts';
export { app } export { app }
if (require.main === module) { if (require.main === module) {
initProxy({
pagesDir: './public',
watch: true,
home: `/root/${HOME}`
});
app.listen(9000, () => { app.listen(9000, () => {
console.log('Server is running on http://localhost:9000'); console.log('Server is running on http://localhost:9000');
}); });
app.onServerRequest(proxyRoute);
} }

0
src/routes/index.ts Normal file
View File

View File

@@ -1,62 +0,0 @@
import { keyboard, Key } from "@nut-tree/nut-js";
// 将快捷键字符串转换为 Key 枚举值
function parseHotkey(hotkey: string): Key[] {
const keyMap: Record<string, Key> = {
'ctrl': Key.LeftControl,
'leftctrl': Key.LeftControl,
'rightctrl': Key.RightControl,
'alt': Key.LeftAlt,
'leftalt': Key.LeftAlt,
'rightalt': Key.RightAlt,
'shift': Key.LeftShift,
'leftshift': Key.LeftShift,
'rightshift': Key.RightShift,
'meta': Key.LeftSuper,
'cmd': Key.LeftCmd,
'win': Key.LeftWin,
};
return hotkey
.toLowerCase()
.split('+')
.map(key => {
const trimmed = key.trim();
// 如果是修饰键,从映射表中获取
if (keyMap[trimmed]) {
return keyMap[trimmed];
}
// 如果是字母,转换为大写并查找对应的 Key
if (trimmed.length === 1 && /[a-z]/.test(trimmed)) {
const upperKey = trimmed.toUpperCase();
return Key[upperKey as keyof typeof Key] as Key;
}
// 其他情况直接查找
return Key[trimmed as keyof typeof Key] as Key;
})
.filter((key): key is Key => key !== undefined);
}
const hotkey = 'ctrl+h';
const keys = parseHotkey(hotkey);
console.log('准备模拟按下快捷键:', hotkey);
console.log('解析后的键:', keys);
// 同时按下所有键
keyboard.pressKey(...keys)
.then(() => {
console.log('快捷键已按下');
// 短暂延迟后释放
return new Promise(resolve => setTimeout(resolve, 100));
})
.then(() => {
// 释放所有键
return keyboard.releaseKey(...keys);
})
.then(() => {
console.log('快捷键已释放');
})
.catch((error) => {
console.error('模拟快捷键失败:', error);
});