zcode-pro 0.17.0 → 0.17.2
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/README.en.md +6 -3
- package/README.md +8 -3
- package/package.json +1 -1
- package/src/host/helper.mjs +1 -1
- package/src/host/main.mjs +33 -3
- package/src/host/paths.mjs +32 -10
- package/src/host/setup.mjs +18 -4
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
ZCode Pro enhances the ZCode desktop UI. No client files are modified; just launch via the ZCode Pro shortcut or the `zcode-pro` terminal command.
|
|
6
6
|
|
|
7
|
-
   ](https://www.npmjs.com/package/zcode-pro) [](https://pypi.org/project/zcode-pro/) [](https://aur.archlinux.org/packages/zcode-pro) [](https://github.com/duanluan/zcode-pro#-windows) [](https://github.com/duanluan/zcode-pro#-linux--macos) [](https://github.com/duanluan/zcode-pro#-linux--macos) [](https://github.com/duanluan/zcode-pro#-community) [](https://github.com/duanluan/zcode-pro#-community)
|
|
8
8
|
|
|
9
9
|
> **Opening settings: after launching ZCode Pro, right-click the settings button (gear) at the bottom of the ZCode sidebar to open the ZCode Pro settings window.**
|
|
10
10
|
|
|
@@ -86,6 +86,8 @@ cd zcode-pro
|
|
|
86
86
|
powershell -ExecutionPolicy Bypass -File scripts\install.ps1
|
|
87
87
|
# Add a Start-menu shortcut + a desktop shortcut
|
|
88
88
|
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Desktop
|
|
89
|
+
# ZCode in a non-default directory that auto-detection misses? Pass its path (persisted, removed on uninstall)
|
|
90
|
+
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Desktop -ZCodePath "D:\ProgramFiles\Zcode\ZCode.exe"
|
|
89
91
|
|
|
90
92
|
# Uninstall
|
|
91
93
|
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Uninstall
|
|
@@ -141,6 +143,7 @@ Configuration and data locations:
|
|
|
141
143
|
| Content | Location |
|
|
142
144
|
| --- | --- |
|
|
143
145
|
| Feature switches, aliases, software proxy | `~/.zcode/zcodepro.json` |
|
|
146
|
+
| Startup diagnostics log (silent shortcut launches, Windows) | `~/.zcode/zcodepro.log` |
|
|
144
147
|
| Global prompt | `~/.zcode/AGENTS.md` |
|
|
145
148
|
| Vision proxy (zcode-vision plugin) | `~/.zcode/zcode-vision.json` |
|
|
146
149
|
| rtk compression (rtk plugin) | `~/.zcode-rtk/` |
|
|
@@ -158,7 +161,7 @@ After uninstalling, just start ZCode from the official entry; no system cleanup
|
|
|
158
161
|
- WeChat group: add **ai4only** on WeChat to be invited
|
|
159
162
|
|
|
160
163
|
<p>
|
|
161
|
-
<img src="assets/qq-group.png" width="200" alt="QQ group QR code" />
|
|
164
|
+
<img src="https://raw.githubusercontent.com/duanluan/zcode-pro/main/assets/qq-group.png" width="200" alt="QQ group QR code" />
|
|
162
165
|
|
|
163
|
-
<img src="assets/wechat-ai4only.png" width="200" alt="WeChat QR code (ai4only)" />
|
|
166
|
+
<img src="https://raw.githubusercontent.com/duanluan/zcode-pro/main/assets/wechat-ai4only.png" width="200" alt="WeChat QR code (ai4only)" />
|
|
164
167
|
</p>
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
ZCode 桌面版界面增强工具。不修改客户端文件,通过 ZCode Pro 快捷方式或终端命令 `zcode-pro` 启动即可。
|
|
6
6
|
|
|
7
|
-
   ](https://www.npmjs.com/package/zcode-pro) [](https://pypi.org/project/zcode-pro/) [](https://aur.archlinux.org/packages/zcode-pro) [](https://github.com/duanluan/zcode-pro#-windows) [](https://github.com/duanluan/zcode-pro#-linux--macos) [](https://github.com/duanluan/zcode-pro#-linux--macos) [](https://github.com/duanluan/zcode-pro#-交流与反馈) [](https://github.com/duanluan/zcode-pro#-交流与反馈)
|
|
8
8
|
|
|
9
9
|
> ⚙️ **打开设置:启动 ZCode Pro 后,右键点击 ZCode 侧边栏底部的设置按钮(齿轮)即可打开 ZCode Pro 设置窗口。**
|
|
10
10
|
|
|
@@ -48,9 +48,11 @@ ZCode 桌面版界面增强工具。不修改客户端文件,通过 ZCode Pro
|
|
|
48
48
|
```bash
|
|
49
49
|
# 方式一:npm 安装/卸载
|
|
50
50
|
npm install -g zcode-pro
|
|
51
|
+
zcode-pro setup --uninstall # 卸载前先撤销快捷方式(如创建过)
|
|
51
52
|
npm uninstall -g zcode-pro
|
|
52
53
|
# 方式二:pip 安装/卸载
|
|
53
54
|
pip install zcode-pro
|
|
55
|
+
zcode-pro setup --uninstall # 卸载前先撤销快捷方式(如创建过)
|
|
54
56
|
pip uninstall zcode-pro
|
|
55
57
|
|
|
56
58
|
# 通过命令启动
|
|
@@ -86,6 +88,8 @@ cd zcode-pro
|
|
|
86
88
|
powershell -ExecutionPolicy Bypass -File scripts\install.ps1
|
|
87
89
|
# 添加开始菜单快捷方式 + 桌面快捷方式
|
|
88
90
|
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Desktop
|
|
91
|
+
# ZCode 装在非默认目录且自动定位不到时,显式指定其路径(持久保存,卸载时清除)
|
|
92
|
+
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Desktop -ZCodePath "D:\ProgramFiles\Zcode\ZCode.exe"
|
|
89
93
|
|
|
90
94
|
# 卸载
|
|
91
95
|
powershell -ExecutionPolicy Bypass -File scripts\install.ps1 -Uninstall
|
|
@@ -141,6 +145,7 @@ bin\zcode-pro.cmd setup
|
|
|
141
145
|
| 内容 | 位置 |
|
|
142
146
|
| --- | --- |
|
|
143
147
|
| 功能开关、项目别名、软件代理 | `~/.zcode/zcodepro.json` |
|
|
148
|
+
| 启动诊断日志(Windows 快捷方式静默启动排查) | `~/.zcode/zcodepro.log` |
|
|
144
149
|
| 全局提示词 | `~/.zcode/AGENTS.md` |
|
|
145
150
|
| 视觉代理(zcode-vision 插件) | `~/.zcode/zcode-vision.json` |
|
|
146
151
|
| rtk 压缩(rtk 插件) | `~/.zcode-rtk/` |
|
|
@@ -158,7 +163,7 @@ bin\zcode-pro.cmd setup
|
|
|
158
163
|
- 微信群:添加微信 **ai4only** 邀请进群
|
|
159
164
|
|
|
160
165
|
<p>
|
|
161
|
-
<img src="assets/qq-group.png" width="200" alt="QQ 群二维码" />
|
|
166
|
+
<img src="https://raw.githubusercontent.com/duanluan/zcode-pro/main/assets/qq-group.png" width="200" alt="QQ 群二维码" />
|
|
162
167
|
|
|
163
|
-
<img src="assets/wechat-ai4only.png" width="200" alt="微信二维码(ai4only)" />
|
|
168
|
+
<img src="https://raw.githubusercontent.com/duanluan/zcode-pro/main/assets/wechat-ai4only.png" width="200" alt="微信二维码(ai4only)" />
|
|
164
169
|
</p>
|
package/package.json
CHANGED
package/src/host/helper.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { pickFolderSystem } from './pickFolder.mjs';
|
|
|
15
15
|
import { reorderWorkspaceTasks, reorderGroupMembers } from './taskOrder.mjs';
|
|
16
16
|
import { webdavRequest, webdavPutWithMkcol, webdavHttpText } from './webdav.mjs';
|
|
17
17
|
|
|
18
|
-
const VERSION = '0.17.
|
|
18
|
+
const VERSION = '0.17.2';
|
|
19
19
|
|
|
20
20
|
// 全局提示词固定在用户主目录:官方加载器按 HOME/USERPROFILE 拼 .zcode/AGENTS.md,
|
|
21
21
|
// 不读 ZCODE_DATA_BASE_DIR(数据根迁走时全局指令仍在原位)。
|
package/src/host/main.mjs
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// 3. 连接 CDP,向所有页面注入增强脚本(新文档自动注入 + 已加载页面立即注入)
|
|
6
6
|
// 4. 启动本地辅助 HTTP 服务并常驻,CDP 断开时自动重连
|
|
7
7
|
import { spawn, execSync, spawnSync } from 'node:child_process';
|
|
8
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
-
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, statSync } from 'node:fs';
|
|
9
|
+
import { dirname, join, resolve } from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
import { randomBytes } from 'node:crypto';
|
|
12
12
|
import { resolveZcodeExecutable, resolveDataRootDir } from './paths.mjs';
|
|
@@ -74,6 +74,26 @@ function guiNotify(title, body) {
|
|
|
74
74
|
} catch { /* ignore */ }
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// Windows 快捷方式静默启动时控制台与桌面通知都不可见(guiNotify 仅 linux 有渠道),
|
|
78
|
+
// 关键启动阶段与致命错误落盘 ~/.zcode/zcodepro.log,便于排查“双击没反应”(issue #1)。
|
|
79
|
+
// 日志超 512KB 时轮转为 .old(zcode-pro.cmd 失败分支有同款轮转,需同步维护)。
|
|
80
|
+
let winLogRotated = false;
|
|
81
|
+
function winLog(line) {
|
|
82
|
+
if (process.platform !== 'win32') return;
|
|
83
|
+
try {
|
|
84
|
+
const dir = resolveDataRootDir();
|
|
85
|
+
mkdirSync(dir, { recursive: true });
|
|
86
|
+
const file = join(dir, 'zcodepro.log');
|
|
87
|
+
if (!winLogRotated) {
|
|
88
|
+
winLogRotated = true;
|
|
89
|
+
try {
|
|
90
|
+
if (existsSync(file) && statSync(file).size > 512 * 1024) renameSync(file, `${file}.old`);
|
|
91
|
+
} catch { /* ignore */ }
|
|
92
|
+
}
|
|
93
|
+
appendFileSync(file, `[${new Date().toISOString()}] ${line}\n`);
|
|
94
|
+
} catch { /* ignore */ }
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
async function cdpAlive(port) {
|
|
78
98
|
try { await fetchBrowserWsUrl(port, 1200); return true; } catch { return false; }
|
|
79
99
|
}
|
|
@@ -147,9 +167,13 @@ export async function run(argv) {
|
|
|
147
167
|
try {
|
|
148
168
|
exe = resolveZcodeExecutable(args.zcodePath);
|
|
149
169
|
} catch (err) {
|
|
150
|
-
if (err.code === 'ZCODE_NOT_FOUND')
|
|
170
|
+
if (err.code === 'ZCODE_NOT_FOUND') {
|
|
171
|
+
winLog('未找到 ZCode 可执行文件: ' + err.message);
|
|
172
|
+
guiNotify('ZCode Pro 启动失败', '未找到 ZCode 可执行文件,详见终端输出。');
|
|
173
|
+
}
|
|
151
174
|
throw err;
|
|
152
175
|
}
|
|
176
|
+
winLog(`启动: ZCode=${exe}, CDP 端口 ${args.cdpPort}`);
|
|
153
177
|
const dataRoot = resolveDataRootDir();
|
|
154
178
|
const token = randomBytes(16).toString('hex');
|
|
155
179
|
const state = { injectedPages: 0 };
|
|
@@ -166,10 +190,12 @@ export async function run(argv) {
|
|
|
166
190
|
if (!ok) {
|
|
167
191
|
console.error(`[zcodepro] 等待 ZCode 调试端口 ${args.cdpPort} 超时。`);
|
|
168
192
|
if (zcodeAppProcessExists()) {
|
|
193
|
+
winLog(`等待 CDP 端口 ${args.cdpPort} 超时:ZCode 已在运行但未开调试端口,需先退出原实例`);
|
|
169
194
|
console.error('[zcodepro] 检测到 ZCode 已在运行,但未开启调试端口,无法注入。');
|
|
170
195
|
console.error('[zcodepro] 请手动退出当前 ZCode(zcodepro 不会替你关闭正在运行的实例),然后重新运行 zcodepro。');
|
|
171
196
|
guiNotify('ZCode Pro 暂无法注入', 'ZCode 已在运行但未开启调试端口,请退出 ZCode 后重新打开 ZCode Pro。');
|
|
172
197
|
} else {
|
|
198
|
+
winLog(`等待 CDP 端口 ${args.cdpPort} 超时(未见 ZCode 进程)`);
|
|
173
199
|
guiNotify('ZCode Pro 启动失败', `等待 ZCode 调试端口 ${args.cdpPort} 超时。`);
|
|
174
200
|
}
|
|
175
201
|
process.exit(1);
|
|
@@ -191,6 +217,7 @@ export async function run(argv) {
|
|
|
191
217
|
healthRes = await fetch(`http://127.0.0.1:${args.helperPort}/health`, { headers: { 'X-ZCodePro-Token': token } }).then((r) => r.json());
|
|
192
218
|
} catch (err) {
|
|
193
219
|
console.error(`[zcodepro] 辅助服务健康检查失败: ${err.message}`);
|
|
220
|
+
winLog('辅助服务健康检查失败: ' + err.message);
|
|
194
221
|
server.close();
|
|
195
222
|
process.exit(1);
|
|
196
223
|
}
|
|
@@ -253,10 +280,12 @@ export async function run(argv) {
|
|
|
253
280
|
}
|
|
254
281
|
if (!conn) {
|
|
255
282
|
console.error('[zcodepro] 无法连接 CDP,退出。');
|
|
283
|
+
winLog('无法连接 CDP,退出');
|
|
256
284
|
server.close();
|
|
257
285
|
process.exit(1);
|
|
258
286
|
}
|
|
259
287
|
console.log('[zcodepro] 已连接 CDP 并开始注入。保持本进程运行以维持增强;Ctrl+C 退出(不影响 ZCode)。');
|
|
288
|
+
winLog('已连接 CDP 并开始注入');
|
|
260
289
|
|
|
261
290
|
process.on('SIGINT', () => { stopping = true; try { conn.close(); } catch {} server.close(); process.exit(0); });
|
|
262
291
|
process.on('SIGTERM', () => { stopping = true; try { conn.close(); } catch {} server.close(); process.exit(0); });
|
|
@@ -268,6 +297,7 @@ export async function run(argv) {
|
|
|
268
297
|
// 连接断开:ZCode 退出则一并退出,否则重连
|
|
269
298
|
if (!(await cdpAlive(args.cdpPort))) {
|
|
270
299
|
console.log('[zcodepro] ZCode 已退出,zcodepro 一并退出。');
|
|
300
|
+
winLog('ZCode 已退出,zcodepro 一并退出');
|
|
271
301
|
server.close();
|
|
272
302
|
process.exit(0);
|
|
273
303
|
}
|
package/src/host/paths.mjs
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
// 跨平台定位 ZCode 桌面版可执行文件。
|
|
2
|
-
// 优先级:--zcode-path 参数 > ZCODEPRO_ZCODE_PATH
|
|
2
|
+
// 优先级:--zcode-path 参数 > ZCODEPRO_ZCODE_PATH 环境变量(进程 > 用户注册表) >
|
|
3
|
+
// 平台默认候选路径 > 注册表 App Paths/卸载表(Windows) > PATH 上的 zcode。
|
|
3
4
|
import { existsSync } from 'node:fs';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
import { arch, homedir, platform } from 'node:os';
|
|
6
7
|
import { execFileSync } from 'node:child_process';
|
|
7
8
|
|
|
9
|
+
// reg 输出按行取 REG_SZ 后的值,容忍引号与 ",0" 图标序号后缀。
|
|
10
|
+
const reg = (args) => {
|
|
11
|
+
try {
|
|
12
|
+
return execFileSync('reg', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
13
|
+
} catch { return ''; }
|
|
14
|
+
};
|
|
15
|
+
const valueAfter = (out) => (out.split(/REG_SZ\s+/)[1] || '').trim();
|
|
16
|
+
|
|
17
|
+
// 安装脚本 -ZCodePath 持久化的用户级环境变量(HKCU\Environment):写入后不会传播
|
|
18
|
+
// 到已运行的资源管理器,进程环境变量读不到时从这里兜底,免注销/重启(issue #1)。
|
|
19
|
+
function windowsUserEnvZcodePath() {
|
|
20
|
+
const value = valueAfter(reg(['query', 'HKCU\\Environment', '/v', 'ZCODEPRO_ZCODE_PATH']));
|
|
21
|
+
return value ? [value] : [];
|
|
22
|
+
}
|
|
23
|
+
|
|
8
24
|
// Windows 注册表探测(结果缓存):自定义安装目录的安装器会在 App Paths(可执行
|
|
9
25
|
// 文件全路径)或卸载表 DisplayIcon(控制面板-卸载软件里可见)留有登记,固定
|
|
10
|
-
// 候选找不到时兜底。
|
|
26
|
+
// 候选找不到时兜底。
|
|
11
27
|
let winRegistryCandidates;
|
|
12
28
|
|
|
13
29
|
function windowsRegistryCandidates() {
|
|
@@ -21,18 +37,16 @@ function windowsRegistryCandidates() {
|
|
|
21
37
|
winRegistryCandidates.push(p);
|
|
22
38
|
}
|
|
23
39
|
};
|
|
24
|
-
const reg = (args) => {
|
|
25
|
-
try {
|
|
26
|
-
return execFileSync('reg', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
27
|
-
} catch { return ''; }
|
|
28
|
-
};
|
|
29
|
-
const valueAfter = (out) => (out.split(/REG_SZ\s+/)[1] || '').trim();
|
|
30
40
|
// App Paths:安装器注册的可执行文件全路径(ShellExecute 同款查找)
|
|
31
41
|
for (const root of ['HKLM', 'HKCU']) {
|
|
32
42
|
const out = reg(['query', `${root}\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\zcode.exe`, '/ve']);
|
|
33
43
|
if (out) push(valueAfter(out));
|
|
34
44
|
}
|
|
35
|
-
// 卸载表 DisplayIcon
|
|
45
|
+
// 卸载表 DisplayIcon:安装目录自定义时通常只有这里能找到。
|
|
46
|
+
// ZCode 的 NSIS 安装器不写 App Paths、DisplayIcon 常指向 uninstallerIcon.ico,
|
|
47
|
+
// 从其所在目录推导 ZCode.exe(win11 虚拟机实测,issue #1)。逐条验证取第一个
|
|
48
|
+
// 有效命中。zcode-pro.cmd 与 install.ps1 也各自实现卸载表定位(匹配方式不完全
|
|
49
|
+
// 相同),改推导规则需三处同步。
|
|
36
50
|
for (const key of [
|
|
37
51
|
'HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
|
|
38
52
|
'HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall',
|
|
@@ -41,7 +55,14 @@ function windowsRegistryCandidates() {
|
|
|
41
55
|
const out = reg(['query', key, '/s', '/v', 'DisplayIcon']);
|
|
42
56
|
for (const line of out.split(/\r?\n/)) {
|
|
43
57
|
const seg = (line.split(/REG_SZ\s+/)[1] || '').trim();
|
|
44
|
-
if (seg && /zcode/i.test(seg))
|
|
58
|
+
if (seg && /zcode/i.test(seg)) {
|
|
59
|
+
push(seg);
|
|
60
|
+
const dir = seg.replace(/[^\\\/]+$/, '');
|
|
61
|
+
if (dir) {
|
|
62
|
+
push(join(dir, 'ZCode.exe'));
|
|
63
|
+
push(join(dir, 'zcode.exe'));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
45
66
|
}
|
|
46
67
|
}
|
|
47
68
|
return winRegistryCandidates;
|
|
@@ -53,6 +74,7 @@ function candidates() {
|
|
|
53
74
|
const localAppData = process.env.LOCALAPPDATA || join(home, 'AppData', 'Local');
|
|
54
75
|
const programFiles = process.env.PROGRAMFILES || 'C:\\Program Files';
|
|
55
76
|
return [
|
|
77
|
+
...windowsUserEnvZcodePath(),
|
|
56
78
|
join(localAppData, 'Programs', 'ZCode', 'ZCode.exe'),
|
|
57
79
|
join(localAppData, 'Programs', 'zcode', 'zcode.exe'),
|
|
58
80
|
join(programFiles, 'ZCode', 'ZCode.exe'),
|
package/src/host/setup.mjs
CHANGED
|
@@ -163,7 +163,7 @@ export function windowsShortcutPlan(cmd, desktop = false, wshOk = true) {
|
|
|
163
163
|
' Next',
|
|
164
164
|
' cmd = cmd & " " & Join(parts, " ")',
|
|
165
165
|
'End If',
|
|
166
|
-
'sh.Run cmd, 0, False',
|
|
166
|
+
'sh.Run "cmd.exe /d /c " & cmd, 0, False',
|
|
167
167
|
'',
|
|
168
168
|
].join('\r\n');
|
|
169
169
|
// 图标复用已安装的 ZCode:探测走 paths.mjs 同一条优先级(含注册表,覆盖自定义
|
|
@@ -232,6 +232,14 @@ function desktopEntry(execCmd) {
|
|
|
232
232
|
].join('\n');
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
// 写入同时设执行位:KDE 等桌面环境要求用户目录(非 root 属主)下的 .desktop 启动器
|
|
236
|
+
// 带执行位才允许启动,缺了双击即被拒(plasmashell/Dolphin 报 executable flag not
|
|
237
|
+
// set),系统级目录(AUR 的 /usr/share/applications)无此要求
|
|
238
|
+
function writeDesktopEntry(target, execCmd) {
|
|
239
|
+
writeFileSync(target, desktopEntry(execCmd));
|
|
240
|
+
chmodSync(target, 0o755);
|
|
241
|
+
}
|
|
242
|
+
|
|
235
243
|
function applicationsDir() {
|
|
236
244
|
return join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'applications');
|
|
237
245
|
}
|
|
@@ -302,16 +310,18 @@ const shortcutItem = {
|
|
|
302
310
|
if (!cur.includes(SETUP_MARK)) {
|
|
303
311
|
return { skipped: `${target} 已存在且非 setup 创建(如 install.sh),未覆盖` };
|
|
304
312
|
}
|
|
313
|
+
// 早期版本写过 0644 的条目:即使内容无需重写也补上执行位(原因见 writeDesktopEntry)
|
|
314
|
+
chmodSync(target, 0o755);
|
|
305
315
|
const exec = (cur.match(/^Exec=(.+)$/m) || [])[1] || '';
|
|
306
316
|
if (exec.includes(cmd)) {
|
|
307
317
|
parts.push('应用菜单条目已存在且指向当前命令,无需重复创建');
|
|
308
318
|
} else {
|
|
309
|
-
|
|
319
|
+
writeDesktopEntry(target, cmd);
|
|
310
320
|
parts.push(`已更新 ${target}(Exec=${cmd})`);
|
|
311
321
|
}
|
|
312
322
|
} else {
|
|
313
323
|
mkdirSync(appsDir, { recursive: true });
|
|
314
|
-
|
|
324
|
+
writeDesktopEntry(target, cmd);
|
|
315
325
|
parts.push(`已写入 ${target}(Exec=${cmd})`);
|
|
316
326
|
}
|
|
317
327
|
try { execFileSync('update-desktop-database', [appsDir], { stdio: 'ignore' }); } catch { /* 无该命令时忽略 */ }
|
|
@@ -320,7 +330,11 @@ const shortcutItem = {
|
|
|
320
330
|
const dDir = desktopDir();
|
|
321
331
|
const dTarget = join(dDir, DESKTOP_FILE);
|
|
322
332
|
if (!existsSync(dTarget) || readFileSync(dTarget, 'utf-8').includes(SETUP_MARK)) {
|
|
323
|
-
|
|
333
|
+
mkdirSync(dDir, { recursive: true });
|
|
334
|
+
writeDesktopEntry(dTarget, cmd);
|
|
335
|
+
// GNOME 的桌面(Nautilus)额外要求 trusted 元数据才显示“允许启动”;
|
|
336
|
+
// 其他环境无 gio / 写不进元数据时忽略
|
|
337
|
+
try { execFileSync('gio', ['set', dTarget, 'metadata::trusted', 'true'], { stdio: 'ignore' }); } catch { /* 非 GNOME 忽略 */ }
|
|
324
338
|
parts.push(`桌面 ${dTarget}`);
|
|
325
339
|
} else {
|
|
326
340
|
parts.push('桌面已存在非 setup 创建的条目,未覆盖');
|