@yangrunchi/a_6 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/A5_/346/270/270/346/210/217/345/256/242/346/210/267/347/253/257/346/250/241/345/235/227/345/274/200/345/217/221/350/247/204/350/214/203.md +26 -13
- package/AutoCfg/A6_generate-tablemgr.js +11 -19
- package/AutoCfg/csvcfg.js +4 -4
- package/AutoCode/A2_generate-panel.js +20 -11
- package/AutoCode/A3_watcher.js +4 -3
- package/AutoNetMsg/A5_generate-funcdata.js +9 -3
- package/AutoNetMsg/A5_sync-netmsg.js +4 -3
- package/SVNUpdate/svn_996.js +2 -1
- package/Tools/install-keys.js +166 -76
- package/Tools/project-root.js +41 -0
- package/Tools/svn-utils.js +56 -6
- package/Tools/uninstall-keys.js +165 -35
- package/bat_manager/index.html +2 -2
- package/bat_manager/local_scripts.json +14 -30
- package/bat_manager/server.js +1 -1
- package/cli.js +28 -19
- package/package.json +1 -1
- package/bat_manager/run-server.bat +0 -4
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
└── {模块文件夹}/
|
|
41
41
|
├── Panel/ -- 存放多个 UI.lua 文件(可选)
|
|
42
42
|
├── Items/ -- 存放 Item 文件,命名以 Item.lua 结尾(可选)
|
|
43
|
-
└── {XXX}
|
|
43
|
+
└── UI{XXX}.lua -- UI 文件必须以 UI{XXX}.lua 开头
|
|
44
44
|
命名示例
|
|
45
45
|
类型 命名格式 示例
|
|
46
46
|
普通面板 UI{XXX}.lua UIBagPanel.lua
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
dev/GUIExport/bag/UIMainBag.lua dev/GUILayout/bag/UIMainBagPanel.lua
|
|
55
55
|
dev/GUIExport/bag/UISubBag.lua dev/GUILayout/bag/UISubBagPanel.lua
|
|
56
56
|
|
|
57
|
-
若以UI{XXX}.lua
|
|
57
|
+
若以UI{XXX}.lua 前缀开头 --> 则首次自动生成GUILayout 目录下的 lua 文件。
|
|
58
58
|
|
|
59
59
|
模版类型 —— 区分单独面板和子面板,减少重复代码
|
|
60
60
|
|
|
@@ -66,20 +66,20 @@
|
|
|
66
66
|
ID = 41600,
|
|
67
67
|
|
|
68
68
|
OBJ_PATH = "GUILayout/R/UIWingPanel",
|
|
69
|
-
UI_PATH = "R/
|
|
69
|
+
UI_PATH = "R/UIWing",
|
|
70
70
|
UI_Name = "翅膀"
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
-- 打开方式
|
|
74
74
|
ssrUIManager:OPEN(ssrObjCfg.Wing)
|
|
75
75
|
```
|
|
76
|
-
#### 类型二:子面板(依赖通用一级背景框
|
|
76
|
+
#### 类型二:子面板(依赖通用一级背景框 UIComOneBgPanel)
|
|
77
77
|
- **适用**:UISub{XXX}.lua
|
|
78
78
|
- **模版**:A4_template_2.txt
|
|
79
79
|
- **目的**:
|
|
80
|
-
依赖通用一级背景框的模版 ,就是把子界面拼接组装到
|
|
80
|
+
依赖通用一级背景框的模版 ,就是把子界面拼接组装到 UIComOneBgPanel 里面,更灵活些。
|
|
81
81
|
- **注意事项**:
|
|
82
|
-
1、通用一级框
|
|
82
|
+
1、通用一级框 UIComOneBgPanel , 包含 遮罩 背景 关闭 问号 页签(有无都支持) 红点 等,能用一级背景框,尽量使用。
|
|
83
83
|
|
|
84
84
|
2、子界面 【垂直水平居中】 推荐使用节点
|
|
85
85
|
第一种:基础容器 + 节点 —— 【N多元素 放2级节点里面】
|
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
OBJ_ORDER = { ssrObjCfg.Mail },
|
|
108
108
|
--OBJ_DESCRIBE = { "41400" }, 没有页签注释
|
|
109
109
|
OBJ_TITLE = { "96000" },
|
|
110
|
-
OBJ_PATH = "GUILayout/R/Com/
|
|
111
|
-
UI_PATH = "R/Com/
|
|
110
|
+
OBJ_PATH = "GUILayout/R/Com/UIComOneBgPanel",
|
|
111
|
+
UI_PATH = "R/Com/UIComOneBg",
|
|
112
112
|
}
|
|
113
113
|
-- 打开方式
|
|
114
114
|
ssrUIManager:OPEN(ssrObjCfg.Mail)
|
|
@@ -163,8 +163,8 @@
|
|
|
163
163
|
OBJ_DESCRIBE = { "强化", "升星", "镶嵌", "洗炼" }, -- OBJ_DESCRIBE 数字 按钮名字是图片 ,否则按钮名字是中文
|
|
164
164
|
OBJ_TITLE = { "1049", "1049", "1049", "1049" },
|
|
165
165
|
|
|
166
|
-
OBJ_PATH = "GUILayout/R/Com/
|
|
167
|
-
UI_PATH = "R/Com/
|
|
166
|
+
OBJ_PATH = "GUILayout/R/Com/UIComOneBgPanel",
|
|
167
|
+
UI_PATH = "R/Com/UIComOneBg",
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
-- 打开方式(自动选中对应页签)
|
|
@@ -253,7 +253,7 @@
|
|
|
253
253
|
ssrRedMgr:SetRed(ssrObjCfg.NiepanZB.ID, isRed, 参数1)
|
|
254
254
|
ssrRedMgr:SetRed(ssrObjCfg.NiepanZB.ID, isRed, 参数2)
|
|
255
255
|
|
|
256
|
-
--
|
|
256
|
+
-- 注意:参考格式为 NiepanData、XiLianData、ChenghaoData 等 Vo 对象
|
|
257
257
|
|
|
258
258
|
```
|
|
259
259
|
|
|
@@ -265,11 +265,24 @@
|
|
|
265
265
|
|
|
266
266
|
### 方式一:VSCode 任务集成 + 快捷键安装脚本
|
|
267
267
|
|
|
268
|
-
**配置文件:** `
|
|
268
|
+
**配置文件:** `a_tools/a_node/Tools/project-keys.json`
|
|
269
269
|
**启用快捷键:终端执行** `node a_tools/a_node/Tools/install-keys.js`
|
|
270
270
|
**禁用快捷键:终端执行** `node a_tools/a_node/Tools/uninstall-keys.js`
|
|
271
271
|
`修改 project-keys.json 后,必须重新执行【启用快捷键】`
|
|
272
272
|
|
|
273
273
|
### 方式二:远程执行脚本(适合非 VSCode 用户)无需安装 VSCode,打开浏览器即可使用
|
|
274
274
|
|
|
275
|
-
### 远程地址:http://10.13.0.26:
|
|
275
|
+
### 远程地址:http://10.13.0.26:9964/
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
cd a_tools/a_node
|
|
279
|
+
npm login # 先登录 npm 账号
|
|
280
|
+
npm publish # 发布
|
|
281
|
+
|
|
282
|
+
npm config set //registry.npmjs.org/:_authToken=ck_fovt6rin8irk.KCuiWzbxfGUfPwFAmFWRVrILcfO9p2bq5yzjHP73ucw
|
|
283
|
+
|
|
284
|
+
#检查 token 是否生效:
|
|
285
|
+
npm whoami
|
|
286
|
+
npm view @yangrunchi/a_6
|
|
287
|
+
npm publish --access public
|
|
288
|
+
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { execSync } = require('child_process');
|
|
9
|
+
const { getSVNAuthor } = require('../Tools/svn-utils');
|
|
9
10
|
|
|
10
11
|
// ==================== 配置 ====================
|
|
12
|
+
const projectRoot = require('../Tools/project-root')();
|
|
11
13
|
|
|
12
14
|
// 目录路径配置
|
|
13
|
-
const csvcfgDir = path.join(
|
|
14
|
-
const mgrDir = path.join(
|
|
15
|
-
const gameConfigDir = path.join(
|
|
15
|
+
const csvcfgDir = path.join(projectRoot, 'dev/GUILayout/ssrgame/csvcfg'); // CSV配置目录
|
|
16
|
+
const mgrDir = path.join(projectRoot, 'dev/GUILayout/ssrgame/mgr'); // 管理器目录
|
|
17
|
+
const gameConfigDir = path.join(projectRoot, 'dev/scripts/game_config'); // 游戏配置目录
|
|
16
18
|
const tableMgrPath = path.join(mgrDir, 'TableMgr.lua'); // 输出文件路径
|
|
17
19
|
|
|
18
20
|
// 标记定义 - 用于识别文件中需要自动更新的区域
|
|
@@ -55,10 +57,14 @@ try {
|
|
|
55
57
|
const configPath = path.join(__dirname, '../AutoCode/A1_996_config.json');
|
|
56
58
|
if (fs.existsSync(configPath)) {
|
|
57
59
|
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
58
|
-
console.log('✅ 已加载配置, 作者:', config.author);
|
|
59
60
|
}
|
|
60
61
|
} catch (err) { }
|
|
61
62
|
|
|
63
|
+
// 自动获取 SVN 账户名(覆盖 config.author)
|
|
64
|
+
const projectRootForAuthor = path.resolve(__dirname, '../../..');
|
|
65
|
+
config.author = getSVNAuthor(projectRootForAuthor, config.author);
|
|
66
|
+
console.log('✅ 已加载配置, 作者:', config.author);
|
|
67
|
+
|
|
62
68
|
// 需要排除的文件(不生成 require 语句)
|
|
63
69
|
const excludeFiles = new Set(['template', 'example', 'test']);
|
|
64
70
|
console.log('\n🚀 开始生成 TableMgr.lua...');
|
|
@@ -74,20 +80,6 @@ function getDateTime() {
|
|
|
74
80
|
|
|
75
81
|
// ==================== SVN 辅助函数 ====================
|
|
76
82
|
|
|
77
|
-
/**
|
|
78
|
-
* 获取 SVN 用户名(当前登录的 SVN 用户)
|
|
79
|
-
*/
|
|
80
|
-
function getSvnUsername() {
|
|
81
|
-
try {
|
|
82
|
-
const output = execSync(`svn info --show-item last-changed-author`, {
|
|
83
|
-
encoding: 'utf8',
|
|
84
|
-
stdio: ['pipe', 'pipe', 'ignore']
|
|
85
|
-
}).trim();
|
|
86
|
-
if (output) return output;
|
|
87
|
-
} catch (err) { }
|
|
88
|
-
return process.env.USERNAME || process.env.USER || config.author || 'unknown';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
83
|
/**
|
|
92
84
|
* 执行 SVN 更新(版本2风格)
|
|
93
85
|
* @param {string} dirPath 目录路径
|
|
@@ -172,7 +164,7 @@ function commitTableMgrToSvn() {
|
|
|
172
164
|
statusOutput.trim().startsWith('A'));
|
|
173
165
|
|
|
174
166
|
if (needCommit) {
|
|
175
|
-
const svnUser =
|
|
167
|
+
const svnUser = config.author;
|
|
176
168
|
const isNewFile = !fs.existsSync(tableMgrPath) || statusOutput.trim().startsWith('A');
|
|
177
169
|
const commitMessage = isNewFile ?
|
|
178
170
|
`[Auto] ${svnUser} -首次创建 TableMgr.lua - ${getDateTime()}` :
|
package/AutoCfg/csvcfg.js
CHANGED
|
@@ -10,8 +10,8 @@ const iconv = require('iconv-lite');
|
|
|
10
10
|
const { execSync } = require('child_process');
|
|
11
11
|
|
|
12
12
|
// ==================== 路径配置 ====================
|
|
13
|
-
const projectRoot =
|
|
14
|
-
const moduleTableRoot = path.join(
|
|
13
|
+
const projectRoot = require('../Tools/project-root')();
|
|
14
|
+
const moduleTableRoot = path.join(projectRoot, '..', 'ModuleTable');
|
|
15
15
|
|
|
16
16
|
// 源文件目录(xlsx 文件)
|
|
17
17
|
const tPath = path.join(moduleTableRoot, 'table');
|
|
@@ -20,10 +20,10 @@ const tPath = path.join(moduleTableRoot, 'table');
|
|
|
20
20
|
const toolPath = path.join(moduleTableRoot, 'tool');
|
|
21
21
|
|
|
22
22
|
// 服务端输出目录
|
|
23
|
-
const sPath = path.join(
|
|
23
|
+
const sPath = path.join(projectRoot, '..', 'MirServer/Mir200/Envir/QuestDiary/cfgcsv');
|
|
24
24
|
|
|
25
25
|
// 客户端输出目录
|
|
26
|
-
const cPath = path.join(
|
|
26
|
+
const cPath = path.join(projectRoot, 'dev/GUILayout/ssrgame/csvcfg');
|
|
27
27
|
|
|
28
28
|
// 打印路径信息
|
|
29
29
|
console.log('========================================');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// -- 作者: 杨润池
|
|
2
|
-
// -- 日期: 2026年
|
|
3
|
-
// -- 版本: 1.
|
|
4
|
-
// -- 描述: 自动生成代码 (支持 UI/UISub/UIMain
|
|
2
|
+
// -- 日期: 2026年06月24日
|
|
3
|
+
// -- 版本: 1.3
|
|
4
|
+
// -- 描述: 自动生成代码 (支持 UI/UISub/UIMain 前缀,作者自动获取 SVN 账户名)
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
@@ -18,6 +18,9 @@ try {
|
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// 导入 SVN 工具函数
|
|
22
|
+
const { getSVNAuthor } = require('../Tools/svn-utils');
|
|
23
|
+
|
|
21
24
|
// 读取 MainUI 模板(默认模板)
|
|
22
25
|
const templatePath = path.join(__dirname, 'A4_template_1.txt');
|
|
23
26
|
let templateMain = '';
|
|
@@ -40,12 +43,14 @@ try {
|
|
|
40
43
|
process.exit(1);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
+
const projectRoot = require('../Tools/project-root')();
|
|
47
|
+
const guiExportDir = path.join(projectRoot, 'dev/GUIExport');
|
|
48
|
+
const guiLayoutDir = path.join(projectRoot, 'dev/GUILayout');
|
|
49
|
+
const author = getSVNAuthor(projectRoot, config.author);
|
|
46
50
|
|
|
47
51
|
console.log(`[DEBUG] guiExportDir: ${guiExportDir}`);
|
|
48
52
|
console.log(`[DEBUG] guiLayoutDir: ${guiLayoutDir}`);
|
|
53
|
+
console.log(`[DEBUG] author: ${author}`);
|
|
49
54
|
|
|
50
55
|
function getDateTime() {
|
|
51
56
|
const d = new Date();
|
|
@@ -63,16 +68,16 @@ function generate(uiFilePath) {
|
|
|
63
68
|
|
|
64
69
|
console.log(`[DEBUG] uiFileName: ${uiFileName}`);
|
|
65
70
|
|
|
66
|
-
//
|
|
67
|
-
const hasUIPrefix = uiFileName.startsWith('UI');
|
|
71
|
+
// 检查文件名是否符合 UI/UISub/UIMain 前缀规则(互斥判断,避免 UI 误匹配 UIMain/UISub)
|
|
68
72
|
const hasUISubPrefix = uiFileName.startsWith('UISub');
|
|
69
73
|
const hasUIMainPrefix = uiFileName.startsWith('UIMain');
|
|
74
|
+
const hasUIPrefix = uiFileName.startsWith('UI') && !hasUISubPrefix && !hasUIMainPrefix;
|
|
70
75
|
|
|
71
76
|
const isValidUI = hasUIPrefix || hasUISubPrefix || hasUIMainPrefix;
|
|
72
77
|
|
|
73
78
|
if (!isValidUI) {
|
|
74
79
|
console.log(`[SKIP] Not a valid UI file: ${uiFileName}`);
|
|
75
|
-
console.log(`[INFO] Expected file starting with: UI,
|
|
80
|
+
console.log(`[INFO] Expected file starting with: UI{XXX}, UIMain{XXX}, or UISub{XXX}`);
|
|
76
81
|
return false;
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -105,16 +110,20 @@ function generate(uiFilePath) {
|
|
|
105
110
|
console.log(`[MKDIR] ${path.relative(guiLayoutDir, dir)}`);
|
|
106
111
|
}
|
|
107
112
|
|
|
108
|
-
//
|
|
113
|
+
// 判断模板类型:UISub → SubUI 模板,UI/UIMain → MainUI 模板
|
|
109
114
|
let selectedTemplate = templateMain;
|
|
110
115
|
let templateType = "MainUI";
|
|
116
|
+
let prefixType = "UI";
|
|
111
117
|
|
|
112
118
|
if (hasUISubPrefix) {
|
|
113
119
|
selectedTemplate = templateSub;
|
|
114
120
|
templateType = "SubUI";
|
|
121
|
+
prefixType = "UISub";
|
|
122
|
+
} else if (hasUIMainPrefix) {
|
|
123
|
+
prefixType = "UIMain";
|
|
115
124
|
}
|
|
116
125
|
|
|
117
|
-
console.log(`[INFO] Using ${templateType} template (prefix: ${
|
|
126
|
+
console.log(`[INFO] Using ${templateType} template (prefix: ${prefixType})`);
|
|
118
127
|
|
|
119
128
|
// 替换模板变量
|
|
120
129
|
const content = selectedTemplate
|
package/AutoCode/A3_watcher.js
CHANGED
|
@@ -20,7 +20,8 @@ if (config.watchEnabled === false) {
|
|
|
20
20
|
process.exit(0);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const projectRoot = require('../Tools/project-root')();
|
|
24
|
+
const watchDir = path.join(projectRoot, 'dev/GUIExport');
|
|
24
25
|
const generateScript = path.join(__dirname, 'A2_generate-panel.js');
|
|
25
26
|
|
|
26
27
|
console.log(`[WATCH] Directory: ${watchDir}`);
|
|
@@ -83,10 +84,10 @@ fs.watch(watchDir, { recursive: true }, (eventType, filename) => {
|
|
|
83
84
|
return;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
//
|
|
87
|
-
const hasUIPrefix = baseName.startsWith('UI');
|
|
87
|
+
// 检查文件名是否符合 UI/UISub/UIMain 前缀规则(互斥判断,避免 UI 误匹配 UIMain/UISub)
|
|
88
88
|
const hasUISubPrefix = baseName.startsWith('UISub');
|
|
89
89
|
const hasUIMainPrefix = baseName.startsWith('UIMain');
|
|
90
|
+
const hasUIPrefix = baseName.startsWith('UI') && !hasUISubPrefix && !hasUIMainPrefix;
|
|
90
91
|
|
|
91
92
|
const isValidUI = hasUIPrefix || hasUISubPrefix || hasUIMainPrefix;
|
|
92
93
|
|
|
@@ -7,11 +7,13 @@ const fs = require('fs');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const { execSync } = require('child_process');
|
|
9
9
|
const os = require('os');
|
|
10
|
+
const { getSVNAuthor } = require('../Tools/svn-utils');
|
|
10
11
|
|
|
11
12
|
// ==================== 配置 ====================
|
|
13
|
+
const projectRoot = require('../Tools/project-root')();
|
|
12
14
|
|
|
13
|
-
const bPath = path.join(
|
|
14
|
-
const ssrgameRoot = path.join(
|
|
15
|
+
const bPath = path.join(projectRoot, 'dev/GUILayout/ssrgame/net/NetMsgCfg.lua');
|
|
16
|
+
const ssrgameRoot = path.join(projectRoot, 'dev/GUILayout/ssrgame');
|
|
15
17
|
const funcDataDir = path.join(ssrgameRoot, 'funcData');
|
|
16
18
|
const mgrDir = path.join(ssrgameRoot, 'mgr');
|
|
17
19
|
|
|
@@ -70,12 +72,16 @@ try {
|
|
|
70
72
|
if (fs.existsSync(configPath)) {
|
|
71
73
|
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
72
74
|
config = JSON.parse(configContent);
|
|
73
|
-
console.log('✅ 已加载配置, 作者:', config.author);
|
|
74
75
|
}
|
|
75
76
|
} catch (err) {
|
|
76
77
|
console.log('⚠️ 未找到配置文件,使用默认配置');
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
// 自动获取 SVN 账户名(覆盖 config.author)
|
|
81
|
+
const projectRootForAuthor = path.resolve(__dirname, '../../..');
|
|
82
|
+
config.author = getSVNAuthor(projectRootForAuthor, config.author);
|
|
83
|
+
console.log('✅ 已加载配置, 作者:', config.author);
|
|
84
|
+
|
|
79
85
|
console.log('🚀 开始生成 funcData 类...');
|
|
80
86
|
console.log('📁 协议文件:', bPath);
|
|
81
87
|
console.log('📁 数据类输出目录:', funcDataDir);
|
|
@@ -9,10 +9,11 @@ const iconv = require('iconv-lite');
|
|
|
9
9
|
const svnUtils = require('../Tools/svn-utils');
|
|
10
10
|
|
|
11
11
|
// ==================== 配置 ====================
|
|
12
|
+
const projectRoot = require('../Tools/project-root')();
|
|
12
13
|
|
|
13
|
-
const NetMsgrPath = path.join(
|
|
14
|
-
const aPath = path.join(
|
|
15
|
-
const bPath = path.join(
|
|
14
|
+
const NetMsgrPath = path.join(projectRoot, '..', 'NetMsg');
|
|
15
|
+
const aPath = path.join(projectRoot, '..', 'MirServer/Mir200/Envir/QuestDiary/net/NetMsgCfg.lua');
|
|
16
|
+
const bPath = path.join(projectRoot, 'dev/GUILayout/ssrgame/net/NetMsgCfg.lua');
|
|
16
17
|
|
|
17
18
|
// 标记定义(统一管理,避免硬编码)
|
|
18
19
|
const MARKERS = {
|
package/SVNUpdate/svn_996.js
CHANGED
|
@@ -8,8 +8,9 @@ const iconv = require('iconv-lite');
|
|
|
8
8
|
const svnUtils = require('../Tools/svn-utils');
|
|
9
9
|
|
|
10
10
|
// ==================== 配置 ====================
|
|
11
|
+
const projectRoot = require('../Tools/project-root')();
|
|
11
12
|
|
|
12
|
-
const ProjecPath = path.join(
|
|
13
|
+
const ProjecPath = path.join(projectRoot, '..');
|
|
13
14
|
|
|
14
15
|
console.log('🚀 开始执行协议同步流程...');
|
|
15
16
|
console.log('📁 当前工作目录:', __dirname);
|
package/Tools/install-keys.js
CHANGED
|
@@ -1,117 +1,207 @@
|
|
|
1
1
|
// -- 作者: 杨润池
|
|
2
2
|
// -- 日期: 2025年10月25日
|
|
3
|
-
// -- 版本: 2.
|
|
4
|
-
// -- 描述:
|
|
3
|
+
// -- 版本: 2.2
|
|
4
|
+
// -- 描述: 多编辑器快捷键同步工具(VS Code / CodeBuddy CN / Cursor)
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
// ============================================================
|
|
10
|
+
// 获取所有已安装编辑器的 keybindings.json 路径
|
|
11
|
+
// ============================================================
|
|
12
|
+
function getEditorPaths() {
|
|
13
|
+
const editors = [];
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
16
|
+
const appData = process.env.APPDATA || (platform === 'darwin'
|
|
17
|
+
? path.join(home, 'Library', 'Application Support')
|
|
18
|
+
: path.join(home, '.config'));
|
|
19
|
+
|
|
20
|
+
function addEditor(name, dirName) {
|
|
21
|
+
const editorPath = path.join(appData, dirName, 'User', 'keybindings.json');
|
|
22
|
+
const editorDir = path.dirname(editorPath);
|
|
23
|
+
if (fs.existsSync(editorDir)) {
|
|
24
|
+
editors.push({ name, path: editorPath });
|
|
17
25
|
}
|
|
26
|
+
}
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
// 按优先级检测编辑器
|
|
29
|
+
addEditor('VS Code', 'Code');
|
|
30
|
+
addEditor('CodeBuddy CN', 'CodeBuddy CN');
|
|
31
|
+
addEditor('CodeBuddy', 'CodeBuddy');
|
|
32
|
+
addEditor('Cursor', 'Cursor');
|
|
33
|
+
|
|
34
|
+
// 如果 CodeBuddy CN 和 CodeBuddy 同时存在,只保留 CN 版本
|
|
35
|
+
const hasCN = editors.some(e => e.name === 'CodeBuddy CN');
|
|
36
|
+
if (hasCN) {
|
|
37
|
+
const idx = editors.findIndex(e => e.name === 'CodeBuddy');
|
|
38
|
+
if (idx !== -1) editors.splice(idx, 1);
|
|
39
|
+
}
|
|
28
40
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
process.env.APPDATA ||
|
|
32
|
-
(process.platform === 'darwin' ? process.env.HOME + '/Library/Application Support' : process.env.HOME + '/.config'),
|
|
33
|
-
'Code/User/keybindings.json'
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
// 4. 确保目录存在
|
|
37
|
-
const globalKeysDir = path.dirname(globalKeysPath);
|
|
38
|
-
if (!fs.existsSync(globalKeysDir)) {
|
|
39
|
-
fs.mkdirSync(globalKeysDir, { recursive: true });
|
|
40
|
-
}
|
|
41
|
+
return editors;
|
|
42
|
+
}
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
// ============================================================
|
|
45
|
+
// 安装快捷键到单个编辑器
|
|
46
|
+
// ============================================================
|
|
47
|
+
function installToEditor(editor, projectKeys, projectName) {
|
|
48
|
+
try {
|
|
49
|
+
console.log(`\n📝 正在配置 ${editor.name}...`);
|
|
50
|
+
console.log(` 路径: ${editor.path}`);
|
|
51
|
+
|
|
52
|
+
// 读取或创建配置
|
|
43
53
|
let globalKeys = [];
|
|
44
|
-
if (fs.existsSync(
|
|
54
|
+
if (fs.existsSync(editor.path)) {
|
|
45
55
|
try {
|
|
46
|
-
const
|
|
47
|
-
globalKeys = JSON.parse(
|
|
56
|
+
const content = fs.readFileSync(editor.path, 'utf8');
|
|
57
|
+
globalKeys = JSON.parse(content);
|
|
48
58
|
if (!Array.isArray(globalKeys)) {
|
|
49
|
-
console.log(
|
|
59
|
+
console.log(` ⚠️ 配置不是数组格式,重置`);
|
|
50
60
|
globalKeys = [];
|
|
51
61
|
}
|
|
52
|
-
} catch (
|
|
53
|
-
console.log(
|
|
62
|
+
} catch (e) {
|
|
63
|
+
console.log(` ⚠️ 读取失败,创建新配置: ${e.message}`);
|
|
54
64
|
globalKeys = [];
|
|
55
65
|
}
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
//
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
console.log(`🗑️ 移除了 ${removedCount} 个已存在的项目快捷键`);
|
|
68
|
+
// 移除本项目已有快捷键(避免重复)
|
|
69
|
+
const before = globalKeys.length;
|
|
70
|
+
globalKeys = globalKeys.filter(item => {
|
|
71
|
+
if (item.when && typeof item.when === 'string') {
|
|
72
|
+
return !item.when.includes(projectName);
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
76
|
+
const removed = before - globalKeys.length;
|
|
77
|
+
if (removed > 0) {
|
|
78
|
+
console.log(` 🗑️ 移除 ${removed} 个已有快捷键`);
|
|
70
79
|
}
|
|
71
80
|
|
|
72
|
-
//
|
|
73
|
-
if (projectKeys.shortcuts
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
// 添加新快捷键
|
|
82
|
+
if (!projectKeys.shortcuts || !Array.isArray(projectKeys.shortcuts)) {
|
|
83
|
+
console.log(` ❌ 缺少 shortcuts 数组`);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
87
|
+
let added = 0;
|
|
88
|
+
projectKeys.shortcuts.forEach((sc, i) => {
|
|
89
|
+
const ns = JSON.parse(JSON.stringify(sc)); // 深拷贝
|
|
82
90
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
// 智能合并 when 条件
|
|
92
|
+
if (!ns.when) {
|
|
93
|
+
ns.when = `resourcePath =~ /${projectName}/`;
|
|
94
|
+
} else if (!ns.when.includes(projectName)) {
|
|
95
|
+
ns.when = `(${ns.when}) && resourcePath =~ /${projectName}/`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
globalKeys.push(ns);
|
|
99
|
+
added++;
|
|
100
|
+
console.log(` ✅ [${i + 1}] ${ns.key} → ${ns.command}`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// 确保目录存在
|
|
104
|
+
const dir = path.dirname(editor.path);
|
|
105
|
+
if (!fs.existsSync(dir)) {
|
|
106
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 写回
|
|
110
|
+
fs.writeFileSync(editor.path, JSON.stringify(globalKeys, null, 4));
|
|
111
|
+
console.log(` ✅ ${editor.name} 完成! 添加 ${added} 个快捷键`);
|
|
112
|
+
return true;
|
|
113
|
+
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.log(` ❌ ${editor.name} 失败: ${error.message}`);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ============================================================
|
|
121
|
+
// 主入口
|
|
122
|
+
// ============================================================
|
|
123
|
+
function installProjectKeys() {
|
|
124
|
+
try {
|
|
125
|
+
console.log('='.repeat(56));
|
|
126
|
+
console.log(' 🚀 项目快捷键同步工具 v2.2');
|
|
127
|
+
console.log('='.repeat(56));
|
|
128
|
+
|
|
129
|
+
// 1. 读取项目配置
|
|
130
|
+
const projectKeysPath = path.join(__dirname, 'project-keys.json');
|
|
131
|
+
if (!fs.existsSync(projectKeysPath)) {
|
|
132
|
+
console.log(`❌ 配置文件不存在: ${projectKeysPath}`);
|
|
133
|
+
console.log('💡 请在同目录下创建 project-keys.json');
|
|
88
134
|
return false;
|
|
89
135
|
}
|
|
90
136
|
|
|
91
|
-
|
|
137
|
+
let projectKeys;
|
|
92
138
|
try {
|
|
93
|
-
|
|
94
|
-
console.log(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.log('❌ 写入全局快捷键配置失败:', error.message);
|
|
139
|
+
projectKeys = JSON.parse(fs.readFileSync(projectKeysPath, 'utf8'));
|
|
140
|
+
console.log('✅ 项目配置读取成功');
|
|
141
|
+
} catch (e) {
|
|
142
|
+
console.log(`❌ JSON 解析失败: ${e.message}`);
|
|
99
143
|
return false;
|
|
100
144
|
}
|
|
101
145
|
|
|
146
|
+
// 2. 项目名称
|
|
147
|
+
const projectName = path.basename(process.cwd());
|
|
148
|
+
console.log(`📁 项目: ${projectName}`);
|
|
149
|
+
console.log(`📋 快捷键数量: ${projectKeys.shortcuts ? projectKeys.shortcuts.length : 0}`);
|
|
150
|
+
|
|
151
|
+
// 3. 显示快捷键列表
|
|
152
|
+
if (projectKeys.shortcuts && projectKeys.shortcuts.length > 0) {
|
|
153
|
+
console.log('\n📌 将要安装:');
|
|
154
|
+
projectKeys.shortcuts.forEach((sc, i) => {
|
|
155
|
+
console.log(` ${i + 1}. ${sc.key} → ${sc.command}${sc.args ? ` (${sc.args})` : ''}`);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 4. 检测编辑器
|
|
160
|
+
const editors = getEditorPaths();
|
|
161
|
+
if (editors.length === 0) {
|
|
162
|
+
console.log('\n⚠️ 未检测到已安装的编辑器');
|
|
163
|
+
console.log('💡 支持: VS Code, CodeBuddy CN, CodeBuddy, Cursor');
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(`\n🔍 检测到 ${editors.length} 个编辑器:`);
|
|
168
|
+
editors.forEach(e => console.log(` ✅ ${e.name}`));
|
|
169
|
+
|
|
170
|
+
// 5. 同步
|
|
171
|
+
console.log('\n🚀 开始同步...');
|
|
172
|
+
console.log('-'.repeat(56));
|
|
173
|
+
|
|
174
|
+
let success = 0;
|
|
175
|
+
editors.forEach(e => {
|
|
176
|
+
if (installToEditor(e, projectKeys, projectName)) success++;
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// 6. 总结
|
|
180
|
+
console.log('\n' + '='.repeat(56));
|
|
181
|
+
console.log(`🎉 同步完成! 成功: ${success}/${editors.length}`);
|
|
182
|
+
console.log('='.repeat(56));
|
|
183
|
+
console.log('💡 可能需要重启编辑器或执行 "Reload Window" 才能生效');
|
|
184
|
+
|
|
185
|
+
return success === editors.length;
|
|
186
|
+
|
|
102
187
|
} catch (error) {
|
|
103
|
-
console.log(
|
|
188
|
+
console.log(`💥 未知错误: ${error.message}`);
|
|
189
|
+
console.error(error);
|
|
104
190
|
return false;
|
|
105
191
|
}
|
|
106
192
|
}
|
|
107
193
|
|
|
108
|
-
//
|
|
194
|
+
// ============================================================
|
|
195
|
+
// 执行
|
|
196
|
+
// ============================================================
|
|
109
197
|
if (require.main === module) {
|
|
110
198
|
installProjectKeys();
|
|
111
199
|
}
|
|
112
200
|
|
|
113
201
|
module.exports = installProjectKeys;
|
|
114
202
|
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
//
|
|
203
|
+
// 使用方法:
|
|
204
|
+
// node a_tools/a_node/Tools/install-keys.js
|
|
205
|
+
// 支持的编辑器:
|
|
206
|
+
// VS Code / CodeBuddy CN / CodeBuddy / Cursor
|
|
207
|
+
// 自动检测已安装的编辑器并同步快捷键
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 项目根路径查找模块
|
|
3
|
+
* 通过查找 996M2_Client.exe 或 env.json 定位项目根目录
|
|
4
|
+
*/
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
|
|
8
|
+
let _cachedRoot = null;
|
|
9
|
+
|
|
10
|
+
function findProjectRoot() {
|
|
11
|
+
if (_cachedRoot) return _cachedRoot;
|
|
12
|
+
|
|
13
|
+
// 优先从环境变量获取(cli.js 会设置)
|
|
14
|
+
if (process.env.M2_PROJECT_ROOT) {
|
|
15
|
+
_cachedRoot = process.env.M2_PROJECT_ROOT;
|
|
16
|
+
return _cachedRoot;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 从当前工作目录向上查找
|
|
20
|
+
let cwd = process.cwd();
|
|
21
|
+
const markers = ['996M2_Client.exe', 'env.json', 'game.dat'];
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < 6; i++) {
|
|
24
|
+
for (const marker of markers) {
|
|
25
|
+
if (fs.existsSync(path.join(cwd, marker))) {
|
|
26
|
+
_cachedRoot = cwd;
|
|
27
|
+
return _cachedRoot;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const parent = path.dirname(cwd);
|
|
31
|
+
if (parent === cwd) break;
|
|
32
|
+
cwd = parent;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 找不到则报错
|
|
36
|
+
console.error('错误: 找不到 996M2_Client 项目根目录');
|
|
37
|
+
console.error('请在 996M2_Client 项目目录下运行此命令');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
module.exports = findProjectRoot;
|
package/Tools/svn-utils.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// -- 作者: 杨润池
|
|
2
2
|
// -- 日期: 2025年10月25日
|
|
3
|
-
// -- 版本: 2.
|
|
4
|
-
// -- 描述: SVN
|
|
3
|
+
// -- 版本: 2.1
|
|
4
|
+
// -- 描述: SVN 工具函数集
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const { execSync, spawnSync } = require('child_process');
|
|
8
|
+
|
|
8
9
|
// 详细检测 SVN 是否可用
|
|
9
10
|
function checkSvnEnvironment() {
|
|
10
11
|
const pathEnv = process.env.PATH || '';
|
|
@@ -98,8 +99,6 @@ function cleanDirectory(targetPath) {
|
|
|
98
99
|
return true;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
// console.log(`🗑️ 清理目录: ${targetPath}`);
|
|
102
|
-
|
|
103
102
|
// 使用 rd /s /q 命令强制删除目录(Windows)
|
|
104
103
|
if (process.platform === 'win32') {
|
|
105
104
|
execSync(`rd /s /q "${targetPath}"`, { stdio: 'inherit' });
|
|
@@ -116,11 +115,62 @@ function cleanDirectory(targetPath) {
|
|
|
116
115
|
}
|
|
117
116
|
}
|
|
118
117
|
|
|
118
|
+
/**
|
|
119
|
+
* 获取当前 SVN 仓库的账户名
|
|
120
|
+
* @param {string} projectRoot - 项目根目录路径
|
|
121
|
+
* @param {string} [fallbackAuthor] - SVN 不可用时回退的作者名
|
|
122
|
+
* @returns {string} SVN 账户名或回退作者名
|
|
123
|
+
*/
|
|
124
|
+
function getSVNAuthor(projectRoot, fallbackAuthor) {
|
|
125
|
+
try {
|
|
126
|
+
// 获取当前仓库的地址(用于匹配 svn auth 中的 realm)
|
|
127
|
+
const reposUrl = execSync(`svn info --show-item=repos-root-url "${projectRoot}"`, {
|
|
128
|
+
encoding: 'utf-8'
|
|
129
|
+
}).trim();
|
|
130
|
+
|
|
131
|
+
// 提取仓库主机地址(如 "192.168.13.64"),兼容有无端口号、不同协议
|
|
132
|
+
const reposHost = reposUrl.replace(/^[a-z]+:\/\//, '').split(':')[0];
|
|
133
|
+
|
|
134
|
+
// 解析 svn auth 输出,匹配当前仓库的用户名
|
|
135
|
+
const authOutput = execSync('svn auth --non-interactive', {
|
|
136
|
+
encoding: 'utf-8'
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// 按分隔线分割每个认证条目
|
|
140
|
+
const entries = authOutput.split(/^-{3,}$/m);
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
const realmMatch = entry.match(/Authentication realm:\s*<([^>]+)>/);
|
|
143
|
+
if (realmMatch) {
|
|
144
|
+
// 提取 realm 中的主机地址进行匹配
|
|
145
|
+
const realmHost = realmMatch[1].replace(/^[a-z]+:\/\//, '').split(':')[0];
|
|
146
|
+
if (realmHost === reposHost) {
|
|
147
|
+
const userMatch = entry.match(/Username:\s*(.+)/);
|
|
148
|
+
if (userMatch) {
|
|
149
|
+
return userMatch[1].trim();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// fallback: 取 svn auth 中最后一个用户名(最近使用的)
|
|
156
|
+
const userLines = authOutput.match(/Username:\s*(.+)/g);
|
|
157
|
+
if (userLines && userLines.length > 0) {
|
|
158
|
+
return userLines[userLines.length - 1].replace(/Username:\s*/, '').trim();
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
// SVN 命令失败时忽略,回退到传入的 fallbackAuthor
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 最终回退:使用传入的回退作者名
|
|
165
|
+
return fallbackAuthor || '';
|
|
166
|
+
}
|
|
167
|
+
|
|
119
168
|
// 导出所有函数
|
|
120
169
|
module.exports = {
|
|
121
170
|
checkSvnEnvironment,
|
|
122
171
|
isSvnAvailable,
|
|
123
172
|
runCommand,
|
|
124
173
|
runCommit,
|
|
125
|
-
cleanDirectory
|
|
126
|
-
|
|
174
|
+
cleanDirectory,
|
|
175
|
+
getSVNAuthor
|
|
176
|
+
};
|
package/Tools/uninstall-keys.js
CHANGED
|
@@ -1,65 +1,195 @@
|
|
|
1
1
|
// -- 作者: 杨润池
|
|
2
2
|
// -- 日期: 2025年10月25日
|
|
3
|
-
// -- 版本: 2.
|
|
4
|
-
// -- 描述:
|
|
3
|
+
// -- 版本: 2.2
|
|
4
|
+
// -- 描述: 多编辑器快捷键卸载工具(VS Code / CodeBuddy CN / Cursor)
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
// ============================================================
|
|
10
|
+
// 获取所有已安装编辑器的 keybindings.json 路径
|
|
11
|
+
// ============================================================
|
|
12
|
+
function getEditorPaths() {
|
|
13
|
+
const editors = [];
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
16
|
+
const appData = process.env.APPDATA || (platform === 'darwin'
|
|
17
|
+
? path.join(home, 'Library', 'Application Support')
|
|
18
|
+
: path.join(home, '.config'));
|
|
19
|
+
|
|
20
|
+
function addEditor(name, dirName) {
|
|
21
|
+
const editorPath = path.join(appData, dirName, 'User', 'keybindings.json');
|
|
22
|
+
const editorDir = path.dirname(editorPath);
|
|
23
|
+
if (fs.existsSync(editorDir)) {
|
|
24
|
+
editors.push({ name, path: editorPath });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
addEditor('VS Code', 'Code');
|
|
29
|
+
addEditor('CodeBuddy CN', 'CodeBuddy CN');
|
|
30
|
+
addEditor('CodeBuddy', 'CodeBuddy');
|
|
31
|
+
addEditor('Cursor', 'Cursor');
|
|
32
|
+
|
|
33
|
+
// 如果 CodeBuddy CN 和 CodeBuddy 同时存在,只保留 CN
|
|
34
|
+
const hasCN = editors.some(e => e.name === 'CodeBuddy CN');
|
|
35
|
+
if (hasCN) {
|
|
36
|
+
const idx = editors.findIndex(e => e.name === 'CodeBuddy');
|
|
37
|
+
if (idx !== -1) editors.splice(idx, 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return editors;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================
|
|
44
|
+
// 从单个编辑器卸载项目快捷键
|
|
45
|
+
// ============================================================
|
|
46
|
+
function uninstallFromEditor(editor, projectName) {
|
|
10
47
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
if (!fs.existsSync(globalKeysPath)) {
|
|
19
|
-
console.log('ℹ️ 全局快捷键配置文件不存在,无需卸载');
|
|
48
|
+
console.log(`\n📝 正在处理 ${editor.name}...`);
|
|
49
|
+
console.log(` 路径: ${editor.path}`);
|
|
50
|
+
|
|
51
|
+
if (!fs.existsSync(editor.path)) {
|
|
52
|
+
console.log(` ℹ️ 配置文件不存在,跳过`);
|
|
20
53
|
return true;
|
|
21
54
|
}
|
|
22
55
|
|
|
23
|
-
//
|
|
56
|
+
// 读取配置
|
|
24
57
|
let globalKeys = [];
|
|
25
58
|
try {
|
|
26
|
-
const
|
|
27
|
-
globalKeys = JSON.parse(
|
|
59
|
+
const content = fs.readFileSync(editor.path, 'utf8');
|
|
60
|
+
globalKeys = JSON.parse(content);
|
|
28
61
|
if (!Array.isArray(globalKeys)) {
|
|
29
|
-
console.log(
|
|
30
|
-
|
|
62
|
+
console.log(` ⚠️ 配置不是数组格式,跳过`);
|
|
63
|
+
return true;
|
|
31
64
|
}
|
|
32
|
-
} catch (
|
|
33
|
-
console.log(
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.log(` ❌ 读取失败: ${e.message}`);
|
|
34
67
|
return false;
|
|
35
68
|
}
|
|
36
69
|
|
|
37
|
-
//
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
70
|
+
// 移除本项目快捷键
|
|
71
|
+
const before = globalKeys.length;
|
|
72
|
+
globalKeys = globalKeys.filter(item => {
|
|
73
|
+
if (item.when && typeof item.when === 'string') {
|
|
74
|
+
return !item.when.includes(projectName);
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
});
|
|
78
|
+
const removed = before - globalKeys.length;
|
|
79
|
+
|
|
80
|
+
// 写回 或 删除空文件
|
|
81
|
+
if (globalKeys.length === 0) {
|
|
82
|
+
fs.unlinkSync(editor.path);
|
|
83
|
+
console.log(` 🗑️ 配置已清空,已删除文件`);
|
|
84
|
+
} else {
|
|
85
|
+
fs.writeFileSync(editor.path, JSON.stringify(globalKeys, null, 4));
|
|
86
|
+
}
|
|
87
|
+
console.log(` ✅ ${editor.name} 卸载完成! 移除 ${removed} 个快捷键`);
|
|
88
|
+
|
|
89
|
+
if (removed === 0) {
|
|
90
|
+
console.log(` ℹ️ 没有找到本项目的快捷键`);
|
|
91
|
+
}
|
|
92
|
+
|
|
50
93
|
return true;
|
|
51
94
|
|
|
52
95
|
} catch (error) {
|
|
53
|
-
console.log(
|
|
96
|
+
console.log(` ❌ ${editor.name} 失败: ${error.message}`);
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================================
|
|
102
|
+
// 主入口
|
|
103
|
+
// ============================================================
|
|
104
|
+
function uninstallProjectKeys() {
|
|
105
|
+
try {
|
|
106
|
+
console.log('='.repeat(56));
|
|
107
|
+
console.log(' 🗑️ 项目快捷键卸载工具 v2.2');
|
|
108
|
+
console.log('='.repeat(56));
|
|
109
|
+
|
|
110
|
+
// 项目名称
|
|
111
|
+
const projectName = path.basename(process.cwd());
|
|
112
|
+
console.log(`📁 项目: ${projectName}`);
|
|
113
|
+
|
|
114
|
+
// 检测编辑器
|
|
115
|
+
const editors = getEditorPaths();
|
|
116
|
+
if (editors.length === 0) {
|
|
117
|
+
console.log('\n⚠️ 未检测到已安装的编辑器');
|
|
118
|
+
console.log('💡 支持: VS Code, CodeBuddy CN, CodeBuddy, Cursor');
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(`\n🔍 检测到 ${editors.length} 个编辑器:`);
|
|
123
|
+
editors.forEach(e => console.log(` ✅ ${e.name}`));
|
|
124
|
+
|
|
125
|
+
// 仅处理存在配置文件的编辑器
|
|
126
|
+
const withConfig = editors.filter(e => fs.existsSync(e.path));
|
|
127
|
+
const noConfig = editors.filter(e => !fs.existsSync(e.path));
|
|
128
|
+
|
|
129
|
+
if (noConfig.length > 0) {
|
|
130
|
+
console.log(`\nℹ️ ${noConfig.length} 个编辑器无配置文件,将跳过:`);
|
|
131
|
+
noConfig.forEach(e => console.log(` - ${e.name}`));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (withConfig.length === 0) {
|
|
135
|
+
console.log('\nℹ️ 所有编辑器都无配置文件,无需卸载');
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 卸载
|
|
140
|
+
console.log('\n🚀 开始卸载...');
|
|
141
|
+
console.log('-'.repeat(56));
|
|
142
|
+
|
|
143
|
+
let success = 0;
|
|
144
|
+
let totalRemoved = 0;
|
|
145
|
+
|
|
146
|
+
// 先统计每个编辑器中要移除的快捷键数量
|
|
147
|
+
withConfig.forEach(e => {
|
|
148
|
+
try {
|
|
149
|
+
const content = fs.readFileSync(e.path, 'utf8');
|
|
150
|
+
const keys = JSON.parse(content);
|
|
151
|
+
if (Array.isArray(keys)) {
|
|
152
|
+
const count = keys.filter(item =>
|
|
153
|
+
item.when && typeof item.when === 'string' && item.when.includes(projectName)
|
|
154
|
+
).length;
|
|
155
|
+
if (count > 0) {
|
|
156
|
+
console.log(` 📋 ${e.name}: 将移除 ${count} 个快捷键`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
} catch (_) { /* ignore */ }
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// 执行卸载
|
|
163
|
+
withConfig.forEach(e => {
|
|
164
|
+
if (uninstallFromEditor(e, projectName)) success++;
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// 总结
|
|
168
|
+
console.log('\n' + '='.repeat(56));
|
|
169
|
+
console.log(`🎉 卸载完成! 处理: ${success}/${withConfig.length} 个编辑器`);
|
|
170
|
+
console.log('='.repeat(56));
|
|
171
|
+
console.log('💡 可能需要重启编辑器或执行 "Reload Window" 才能生效');
|
|
172
|
+
|
|
173
|
+
return success === withConfig.length;
|
|
174
|
+
|
|
175
|
+
} catch (error) {
|
|
176
|
+
console.log(`💥 未知错误: ${error.message}`);
|
|
177
|
+
console.error(error);
|
|
54
178
|
return false;
|
|
55
179
|
}
|
|
56
180
|
}
|
|
57
181
|
|
|
58
|
-
//
|
|
182
|
+
// ============================================================
|
|
183
|
+
// 执行
|
|
184
|
+
// ============================================================
|
|
59
185
|
if (require.main === module) {
|
|
60
186
|
uninstallProjectKeys();
|
|
61
187
|
}
|
|
62
188
|
|
|
63
189
|
module.exports = uninstallProjectKeys;
|
|
64
190
|
|
|
65
|
-
//
|
|
191
|
+
// 使用方法:
|
|
192
|
+
// node a_tools/a_node/Tools/uninstall-keys.js
|
|
193
|
+
// 支持的编辑器:
|
|
194
|
+
// VS Code / CodeBuddy CN / CodeBuddy / Cursor
|
|
195
|
+
// 自动检测并移除所有编辑器中的本项目快捷键
|
package/bat_manager/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
6
|
-
<title
|
|
6
|
+
<title>盟约传奇 — 一键工具</title>
|
|
7
7
|
<style>
|
|
8
8
|
* {
|
|
9
9
|
box-sizing: border-box;
|
|
@@ -594,7 +594,7 @@
|
|
|
594
594
|
let localTaskRunning = false; // 本地发起任务是否在执行
|
|
595
595
|
let pendingEndTimer = null;
|
|
596
596
|
|
|
597
|
-
const WS_URL = `ws://${window.location.hostname}:
|
|
597
|
+
const WS_URL = `ws://${window.location.hostname}:9964`;
|
|
598
598
|
|
|
599
599
|
// DOM 元素
|
|
600
600
|
const authOverlay = document.getElementById('authOverlay');
|
|
@@ -1,42 +1,26 @@
|
|
|
1
1
|
[
|
|
2
2
|
{
|
|
3
|
-
"id": "
|
|
4
|
-
"name": "
|
|
5
|
-
"path": "
|
|
3
|
+
"id": "1782186651373",
|
|
4
|
+
"name": "一键配置",
|
|
5
|
+
"path": "G:\\MengYueChuanQi996\\Project\\996M2_Client\\a_tools\\a_node\\run-csvcfg.bat",
|
|
6
6
|
"args": "",
|
|
7
|
-
"addedBy": "
|
|
8
|
-
"addedAt": "2026-
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"id": "1776937917012",
|
|
12
|
-
"name": "同步协议",
|
|
13
|
-
"path": "H:/NiePanChuanQi/Project/996M2_Client/a_tools/a_node/run-sync.bat",
|
|
14
|
-
"args": "",
|
|
15
|
-
"addedBy": "qqww123",
|
|
16
|
-
"addedAt": "2026-04-23T09:51:57.012Z"
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
"id": "1777254823830",
|
|
20
|
-
"name": "启动客服端",
|
|
21
|
-
"path": "H:/NiePanChuanQi/Project/996M2_Client/a_tools/a_node/run-client.bat",
|
|
22
|
-
"args": "",
|
|
23
|
-
"addedBy": "yrc001",
|
|
24
|
-
"addedAt": "2026-04-27T01:53:43.830Z"
|
|
7
|
+
"addedBy": "admin",
|
|
8
|
+
"addedAt": "2026-06-23T03:50:51.373Z"
|
|
25
9
|
},
|
|
26
10
|
{
|
|
27
|
-
"id": "
|
|
28
|
-
"name": "
|
|
29
|
-
"path": "
|
|
11
|
+
"id": "1782186676214",
|
|
12
|
+
"name": "一键协议",
|
|
13
|
+
"path": "G:/MengYueChuanQi996/Project/996M2_Client/a_tools/a_node/run-sync.bat",
|
|
30
14
|
"args": "",
|
|
31
|
-
"addedBy": "
|
|
32
|
-
"addedAt": "2026-
|
|
15
|
+
"addedBy": "admin",
|
|
16
|
+
"addedAt": "2026-06-23T03:51:16.214Z"
|
|
33
17
|
},
|
|
34
18
|
{
|
|
35
|
-
"id": "
|
|
36
|
-
"name": "
|
|
37
|
-
"path": "
|
|
19
|
+
"id": "1782186725524",
|
|
20
|
+
"name": "9964工程(📁 G:/MengYueChuanQi996/Project)SVN更新",
|
|
21
|
+
"path": "G:/MengYueChuanQi996/Project/996M2_Client/a_tools/a_node/run-svn.bat",
|
|
38
22
|
"args": "",
|
|
39
23
|
"addedBy": "admin",
|
|
40
|
-
"addedAt": "2026-06-
|
|
24
|
+
"addedAt": "2026-06-23T03:52:05.524Z"
|
|
41
25
|
}
|
|
42
26
|
]
|
package/bat_manager/server.js
CHANGED
package/cli.js
CHANGED
|
@@ -24,30 +24,35 @@ if (!commands[action]) {
|
|
|
24
24
|
process.exit(0);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// 特殊处理:启动客户端需要找到项目根目录的 exe
|
|
30
|
-
if (action === '1') {
|
|
31
|
-
console.log(`🚀 执行: ${cmd.desc}`);
|
|
32
|
-
// 从当前工作目录向上查找 996M2_Client.exe
|
|
27
|
+
// 从当前工作目录向上查找项目根目录
|
|
28
|
+
function findProjectRoot() {
|
|
33
29
|
let cwd = process.cwd();
|
|
34
|
-
|
|
35
|
-
for (let i = 0; i <
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
const markers = ['996M2_Client.exe', 'env.json', 'game.dat'];
|
|
31
|
+
for (let i = 0; i < 6; i++) {
|
|
32
|
+
for (const marker of markers) {
|
|
33
|
+
if (fs.existsSync(path.join(cwd, marker))) {
|
|
34
|
+
return cwd;
|
|
35
|
+
}
|
|
40
36
|
}
|
|
41
37
|
const parent = path.dirname(cwd);
|
|
42
|
-
if (parent === cwd) break;
|
|
38
|
+
if (parent === cwd) break;
|
|
43
39
|
cwd = parent;
|
|
44
40
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const projectRoot = findProjectRoot();
|
|
45
|
+
const cmd = commands[action];
|
|
46
|
+
|
|
47
|
+
if (action === '1') {
|
|
48
|
+
// 特殊处理:启动客户端
|
|
49
|
+
if (!projectRoot) {
|
|
50
|
+
console.error('错误: 找不到 996M2_Client 项目目录');
|
|
51
|
+
console.error('请在项目目录下运行 a_6 1');
|
|
49
52
|
process.exit(1);
|
|
50
53
|
}
|
|
54
|
+
const exePath = path.join(projectRoot, '996M2_Client.exe');
|
|
55
|
+
console.log(`🚀 执行: ${cmd.desc}`);
|
|
51
56
|
console.log(`启动 996M2 客户端...`);
|
|
52
57
|
console.log(`路径: ${exePath}`);
|
|
53
58
|
exec(`start "" "${exePath}"`, (err) => {
|
|
@@ -57,10 +62,14 @@ if (action === '1') {
|
|
|
57
62
|
}
|
|
58
63
|
console.log('客户端已启动');
|
|
59
64
|
});
|
|
60
|
-
// 给异步回调一点时间
|
|
61
65
|
setTimeout(() => process.exit(0), 2000);
|
|
62
66
|
} else {
|
|
67
|
+
// 设置环境变量,让子脚本也能找到项目根目录
|
|
68
|
+
const env = { ...process.env };
|
|
69
|
+
if (projectRoot) {
|
|
70
|
+
env.M2_PROJECT_ROOT = projectRoot;
|
|
71
|
+
}
|
|
63
72
|
const batFile = path.join(__dirname, cmd.file);
|
|
64
73
|
console.log(`🚀 执行: ${cmd.desc}`);
|
|
65
|
-
execSync(`"${batFile}"`, { stdio: 'inherit', shell: true });
|
|
74
|
+
execSync(`"${batFile}"`, { stdio: 'inherit', shell: true, env });
|
|
66
75
|
}
|
package/package.json
CHANGED