zen-gitsync 2.14.1 → 2.14.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/package.json +1 -1
- package/src/ui/public/assets/{AppVersionBadge-zv0-I77-.js → AppVersionBadge-M9ydXpd9.js} +1 -1
- package/src/ui/public/assets/{BranchSelector-D7-iEofR.js → BranchSelector-scIk6OeM.js} +1 -1
- package/src/ui/public/assets/{CommandConsole-Bu6N8qeh.js → CommandConsole-BikQuarw.js} +2 -2
- package/src/ui/public/assets/{CommitForm-C11gIhY6.js → CommitForm-DuvNxUdP.js} +1 -1
- package/src/ui/public/assets/{EditorView-Ce6CTr9d.js → EditorView-rGLnccH3.js} +0 -0
- package/src/ui/public/assets/{FlowExecutionViewer-DA_gtjWd.js → FlowExecutionViewer-B4BADP8e.js} +1 -1
- package/src/ui/public/assets/{FlowOrchestrationWorkspace-BJ6tqbOP.js → FlowOrchestrationWorkspace-C3l3Jco7.js} +1 -1
- package/src/ui/public/assets/{LogList-DYGfJdcb.js → LogList-7agk5Obq.js} +1 -1
- package/src/ui/public/assets/MindmapView-B8rUSi-e.css +1 -0
- package/src/ui/public/assets/MindmapView-mOFG9TmR.js +1 -0
- package/src/ui/public/assets/{ProjectStartupButton-C3LM1U1R.js → ProjectStartupButton-CgS14qyg.js} +1 -1
- package/src/ui/public/assets/{RemoteRepoCard-ebapcxCN.js → RemoteRepoCard-CFKl6CB9.js} +1 -1
- package/src/ui/public/assets/{SourceMapView-CFTKhZQi.js → SourceMapView-Dsc_a3t7.js} +1 -1
- package/src/ui/public/assets/{UserInputNode-CfmSt5Mh.js → UserInputNode-yBT2005h.js} +1 -1
- package/src/ui/public/assets/{WorkbenchView-kAXgcs6z.js → WorkbenchView-CiG-qMl7.js} +1 -1
- package/src/ui/public/assets/configStore-Cq6Qwn35.js +1 -0
- package/src/ui/public/assets/{index-C1q8_YYO.js → index-CPqKvZBK.js} +3 -3
- package/src/ui/public/index.html +2 -2
- package/src/ui/server/index.js +130 -2
- package/src/ui/server/routes/gitOps.js +8 -4
- package/src/utils/index.js +165 -12
- package/src/ui/public/assets/MindmapView-Az3mQBEW.js +0 -1
- package/src/ui/public/assets/MindmapView-RpSZW35N.css +0 -1
- package/src/ui/public/assets/configStore-DHCrsR6z.js +0 -1
package/src/ui/public/index.html
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
11
11
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
12
12
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-CPqKvZBK.js"></script>
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CMxvf4Kt.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/monaco-BFLoT2VD.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/element-plus-B6Owr5jY.js">
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/assets/vue-flow-C6RXt-U3.js">
|
|
21
21
|
<link rel="modulepreload" crossorigin href="/assets/SvgIcon-Cw2mL_zx.js">
|
|
22
22
|
<link rel="modulepreload" crossorigin href="/assets/CommonDialog-G17HYMln.js">
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/configStore-
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/configStore-Cq6Qwn35.js">
|
|
24
24
|
<link rel="stylesheet" crossorigin href="/assets/monaco-B2lPvkrc.css">
|
|
25
25
|
<link rel="stylesheet" crossorigin href="/assets/element-plus-D06ZgZjR.css">
|
|
26
26
|
<link rel="stylesheet" crossorigin href="/assets/vendor-DNI_idR2.css">
|
package/src/ui/server/index.js
CHANGED
|
@@ -63,6 +63,111 @@ let processIdCounter = 0;
|
|
|
63
63
|
const terminalSessions = new Map(); // key: terminalSessionId, value: { id, command, workingDirectory, pid, createdAt, lastStartedAt }
|
|
64
64
|
let terminalSessionIdCounter = 0;
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* 终止并等待单个子进程退出
|
|
68
|
+
* @param {import('child_process').ChildProcess} child
|
|
69
|
+
* @param {number} timeoutMs 等待 SIGTERM 生效的最大时间,超时后发 SIGKILL
|
|
70
|
+
* @returns {Promise<void>}
|
|
71
|
+
*/
|
|
72
|
+
function killChildProcess(child, timeoutMs = 3000) {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
if (!child || (child.exitCode !== null && child.exitCode !== undefined)) {
|
|
75
|
+
return resolve();
|
|
76
|
+
}
|
|
77
|
+
let done = false;
|
|
78
|
+
const finish = () => {
|
|
79
|
+
if (done) return;
|
|
80
|
+
done = true;
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
child.removeListener('exit', finish);
|
|
83
|
+
resolve();
|
|
84
|
+
};
|
|
85
|
+
child.once('exit', finish);
|
|
86
|
+
try {
|
|
87
|
+
// SIGTERM 优雅退出,被 Node 在 Windows 上转 TerminateProcess
|
|
88
|
+
child.kill('SIGTERM');
|
|
89
|
+
} catch (_) {
|
|
90
|
+
finish();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// 超时未退就 SIGKILL 强杀
|
|
94
|
+
const timer = setTimeout(() => {
|
|
95
|
+
try { child.kill('SIGKILL'); } catch (_) {}
|
|
96
|
+
// 给 SIGKILL 200ms 兜底,再不退也不等了
|
|
97
|
+
setTimeout(finish, 200);
|
|
98
|
+
}, timeoutMs);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 并行终止 runningProcesses 内所有子进程,总等待时间不超过 timeoutMs。
|
|
104
|
+
* 防止 SIGINT/SIGTERM 关闭 server 时,正在跑的 claude/npm/git 子进程变孤儿。
|
|
105
|
+
*
|
|
106
|
+
* 注意:返回值是"尝试终止的进程数",不等于"已实际退出数"。内部用
|
|
107
|
+
* Promise.race 卡总时长,timeoutMs 到了就返回,此时可能仍有子进程
|
|
108
|
+
* 处于 SIGTERM → exit 的过渡中(尤其 Windows 上 TerminateProcess 是
|
|
109
|
+
* 异步的)。调用方日志应使用"已尝试终止"措辞,避免排查孤儿进程时被误导。
|
|
110
|
+
*
|
|
111
|
+
* @param {number} timeoutMs
|
|
112
|
+
* @returns {Promise<number>} 被尝试终止的进程数(entries.length)
|
|
113
|
+
*/
|
|
114
|
+
async function drainRunningProcesses(timeoutMs = 5000) {
|
|
115
|
+
const entries = Array.from(runningProcesses.entries());
|
|
116
|
+
if (entries.length === 0) return 0;
|
|
117
|
+
// 先并行发 SIGTERM,再并行等;整批用 Promise.race 卡最大时长
|
|
118
|
+
const killPromises = entries.map(([, info]) => killChildProcess(info.childProcess, timeoutMs).catch(() => {}));
|
|
119
|
+
await Promise.race([
|
|
120
|
+
Promise.all(killPromises),
|
|
121
|
+
new Promise((r) => setTimeout(r, timeoutMs))
|
|
122
|
+
]);
|
|
123
|
+
return entries.length;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 全局错误处理 — 幂等,只允许进入 fatalExit 一次
|
|
127
|
+
let _fatalExitInProgress = false;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 注册 process.on('uncaughtException') / unhandledRejection 处理器
|
|
131
|
+
*
|
|
132
|
+
* 触发时的清理链路(防止留下孤儿子进程 / 假死 registry / 端口文件残留):
|
|
133
|
+
* 1) drainRunningProcesses(3s) — 并行 SIGTERM 杀掉所有 claude/npm/git 子进程
|
|
134
|
+
* 2) instanceRegistry.unregister — 把自己从实例表里摘掉
|
|
135
|
+
* 3) setTimeout 250ms 退 — 给 logger/console 一点时间把日志刷盘
|
|
136
|
+
*
|
|
137
|
+
* @param {Object} opts
|
|
138
|
+
* @param {ReturnType<typeof createInstanceRegistry>} opts.instanceRegistry
|
|
139
|
+
*/
|
|
140
|
+
function setupGlobalErrorHandlers({ instanceRegistry }) {
|
|
141
|
+
// 幂等:多个错误同时触发时只走一次清理,避免清理动作自身再次触发 fatalExit
|
|
142
|
+
const fatalExit = async (reason, err) => {
|
|
143
|
+
if (_fatalExitInProgress) return;
|
|
144
|
+
_fatalExitInProgress = true;
|
|
145
|
+
try {
|
|
146
|
+
logger.error(`[FATAL] ${reason}: ${err?.message || err}`);
|
|
147
|
+
if (err?.stack) logger.error(err.stack);
|
|
148
|
+
} catch (_) { /* logger 自身炸了,放弃 */ }
|
|
149
|
+
// 1) 杀光所有子进程
|
|
150
|
+
try {
|
|
151
|
+
const killed = await drainRunningProcesses(3000);
|
|
152
|
+
if (killed > 0) logger.warn(`[FATAL] 已尝试终止 ${killed} 个运行中的子进程`);
|
|
153
|
+
} catch (_) {}
|
|
154
|
+
// 2) 反注册实例
|
|
155
|
+
if (instanceRegistry) {
|
|
156
|
+
try { await instanceRegistry.unregister(process.pid); } catch (_) {}
|
|
157
|
+
}
|
|
158
|
+
// 3) 给 OS 250ms 刷盘日志,然后非 0 退出
|
|
159
|
+
setTimeout(() => process.exit(1), 250);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
process.on('uncaughtException', (err, origin) => {
|
|
163
|
+
// 同步处理路径:直接调 fatalExit,内部 await 会进入 microtask
|
|
164
|
+
fatalExit(`uncaughtException (${origin || 'uncaught'})`, err);
|
|
165
|
+
});
|
|
166
|
+
process.on('unhandledRejection', (reason) => {
|
|
167
|
+
fatalExit('unhandledRejection', reason instanceof Error ? reason : new Error(String(reason)));
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
66
171
|
// 分支状态缓存
|
|
67
172
|
let branchStatusCache = {
|
|
68
173
|
currentBranch: null,
|
|
@@ -147,6 +252,12 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
147
252
|
registryPath: path.join(os.homedir(), '.zen-gitsync-instances.json')
|
|
148
253
|
});
|
|
149
254
|
|
|
255
|
+
// 注册全局错误处理器:任意未捕获异常/拒绝都会触发 fatalExit,
|
|
256
|
+
// 走 drainRunningProcesses + unregister + 延迟 250ms 退出的链路。
|
|
257
|
+
// 在 instanceRegistry 创建之后立即注册,这样后续 route 注册阶段抛出
|
|
258
|
+
// 也能被同一个处理器捕获(否则会无声崩,留下子进程孤儿与 .port 残留)。
|
|
259
|
+
setupGlobalErrorHandlers({ instanceRegistry });
|
|
260
|
+
|
|
150
261
|
// 添加全局中间件来解析JSON请求体
|
|
151
262
|
app.use(express.json());
|
|
152
263
|
|
|
@@ -478,11 +589,28 @@ async function startUIServer(noOpen = false, savePort = false) {
|
|
|
478
589
|
}
|
|
479
590
|
}, fsSync.watch);
|
|
480
591
|
|
|
481
|
-
// 优雅退出:SIGINT/SIGTERM
|
|
592
|
+
// 优雅退出:SIGINT/SIGTERM 触发 drain 子进程 + unregister + 清心跳
|
|
593
|
+
// 幂等保护:连按 Ctrl+C 或 SIGINT+SIGTERM 同来时只走一次清理,
|
|
594
|
+
// 避免重复 drain(3s)、重复 unregister、排多个 setTimeout(exit)。
|
|
595
|
+
// 与上方 _fatalExitInProgress 同思路,但作用域在本次 startUIServer 调用内。
|
|
596
|
+
let _shuttingDown = false;
|
|
482
597
|
const shutdown = async (signal) => {
|
|
598
|
+
if (_shuttingDown) return;
|
|
599
|
+
_shuttingDown = true;
|
|
600
|
+
console.log(chalk.gray(`[shutdown] 收到 ${signal},开始清理…`));
|
|
601
|
+
// 1) 先并行 SIGTERM 所有正在跑的子进程(限时 3s),防止 claude/npm/git 变孤儿。
|
|
602
|
+
// 注意:返回值是"尝试终止数"而非"实际终止数" — drain 内部用
|
|
603
|
+
// Promise.race 卡总时长,3s 到了就返回,可能仍有进程在 SIGTERM 退出中。
|
|
604
|
+
try {
|
|
605
|
+
const killed = await drainRunningProcesses(3000);
|
|
606
|
+
if (killed > 0) console.log(chalk.gray(`[shutdown] 已尝试终止 ${killed} 个子进程(实际终止情况以 exit 事件为准)`));
|
|
607
|
+
} catch (_) {}
|
|
608
|
+
// 2) 清心跳 + 反注册实例
|
|
483
609
|
try { clearInterval(heartbeatTimer); } catch (_) {}
|
|
484
610
|
try { await instanceRegistry.unregister(process.pid); } catch (_) {}
|
|
485
|
-
console.log(chalk.gray(`[
|
|
611
|
+
console.log(chalk.gray(`[shutdown] 收到 ${signal},已清理本实例`));
|
|
612
|
+
// 3) 给 OS 100ms 刷盘日志
|
|
613
|
+
setTimeout(() => process.exit(0), 100);
|
|
486
614
|
};
|
|
487
615
|
process.on('SIGINT', () => { shutdown('SIGINT'); });
|
|
488
616
|
process.on('SIGTERM', () => { shutdown('SIGTERM'); });
|
|
@@ -664,8 +664,10 @@ export function registerGitOpsRoutes({
|
|
|
664
664
|
});
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
-
// 尝试清理 Git 锁文件
|
|
668
|
-
|
|
667
|
+
// 尝试清理 Git 锁文件(破坏性入口:force=true 绕过 mtime 判断,
|
|
668
|
+
// 仍走 PID liveness 校验——用户主动触发的 reset --hard 不该被
|
|
669
|
+
// 一个刚崩出来的新孤儿锁挡住)
|
|
670
|
+
await checkAndClearGitLock({ force: true });
|
|
669
671
|
|
|
670
672
|
// 执行 git reset --hard origin/branch 命令
|
|
671
673
|
await execGitCommand(['reset', '--hard', `origin/${branch}`]);
|
|
@@ -682,8 +684,10 @@ export function registerGitOpsRoutes({
|
|
|
682
684
|
// 清除本地所有更改,包括未跟踪文件 (git reset --hard && git clean -fd)
|
|
683
685
|
app.post('/api/discard-all-changes', async (req, res) => {
|
|
684
686
|
try {
|
|
685
|
-
// 尝试清理 Git 锁文件
|
|
686
|
-
|
|
687
|
+
// 尝试清理 Git 锁文件(破坏性入口:force=true 绕过 mtime 判断,
|
|
688
|
+
// 仍走 PID liveness 校验——用户主动触发的 discard-all 不该被
|
|
689
|
+
// 一个刚崩出来的新孤儿锁挡住)
|
|
690
|
+
await checkAndClearGitLock({ force: true });
|
|
687
691
|
|
|
688
692
|
// 1. 执行 git reset --hard 丢弃已跟踪文件的更改
|
|
689
693
|
await execGitCommand(['reset', '--hard']);
|
package/src/utils/index.js
CHANGED
|
@@ -180,9 +180,45 @@ function clearCommandHistory() {
|
|
|
180
180
|
return true;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
// 按 git 子命令类型设定默认超时(毫秒),避免 git push/fetch 撞死网导致
|
|
184
|
+
// 整个 Node 事件循环永久挂起。读类操作给短超时(快);网络类给长超时;
|
|
185
|
+
// 默认 60s 兜底。调用方可通过 options.timeout 显式覆盖,timeout=0 表示不超时。
|
|
186
|
+
const DEFAULT_GIT_TIMEOUT_MS = {
|
|
187
|
+
// 只读 metadata,通常几 ms 完成
|
|
188
|
+
status: 30_000,
|
|
189
|
+
'rev-parse': 10_000,
|
|
190
|
+
branch: 10_000,
|
|
191
|
+
'rev-list': 30_000,
|
|
192
|
+
// 输出可能很大
|
|
193
|
+
log: 30_000,
|
|
194
|
+
diff: 60_000,
|
|
195
|
+
show: 30_000,
|
|
196
|
+
// 写操作,锁文件 + 索引更新
|
|
197
|
+
add: 60_000,
|
|
198
|
+
commit: 60_000,
|
|
199
|
+
// 网络操作,留足时间应对慢网 + 钩子 + 大仓库
|
|
200
|
+
fetch: 300_000,
|
|
201
|
+
pull: 300_000,
|
|
202
|
+
push: 300_000,
|
|
203
|
+
clone: 600_000,
|
|
204
|
+
remote: 60_000,
|
|
205
|
+
// 兜底
|
|
206
|
+
default: 60_000,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
function getDefaultGitTimeout(command) {
|
|
210
|
+
if (!Array.isArray(command) || command.length === 0) return DEFAULT_GIT_TIMEOUT_MS.default;
|
|
211
|
+
const sub = String(command[0] || '').trim();
|
|
212
|
+
return DEFAULT_GIT_TIMEOUT_MS[sub] ?? DEFAULT_GIT_TIMEOUT_MS.default;
|
|
213
|
+
}
|
|
214
|
+
|
|
183
215
|
function execGitCommand(command, options = {}) {
|
|
184
216
|
return new Promise((resolve, reject) => {
|
|
185
|
-
let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = Array.isArray(command) ? command.join(' ') : command, log = true} = options
|
|
217
|
+
let {encoding = 'utf-8', maxBuffer = 30 * 1024 * 1024, head = Array.isArray(command) ? command.join(' ') : command, log = true, timeout} = options
|
|
218
|
+
// options.timeout === undefined → 按子命令类型取默认;0 = 不超时(显式禁用)
|
|
219
|
+
if (timeout === undefined) {
|
|
220
|
+
timeout = getDefaultGitTimeout(command);
|
|
221
|
+
}
|
|
186
222
|
let cwd = getCwd()
|
|
187
223
|
|
|
188
224
|
// Record start time for command execution
|
|
@@ -193,6 +229,7 @@ function execGitCommand(command, options = {}) {
|
|
|
193
229
|
// - 不走 shell,Windows cmd.exe / POSIX sh 都一致
|
|
194
230
|
// - argv 数组天然免疫 shell 注入,文件名/参数无需 shellQuote
|
|
195
231
|
// - 所有调用点约定只跑 git 子命令,见 src/utils/index.js 内的 execGitCommand 调用清单
|
|
232
|
+
// - timeout 显式传 0 表示禁用;否则按 getDefaultGitTimeout() 自动选值
|
|
196
233
|
execFile('git', command, {
|
|
197
234
|
env: {
|
|
198
235
|
...process.env,
|
|
@@ -205,7 +242,9 @@ function execGitCommand(command, options = {}) {
|
|
|
205
242
|
cwd,
|
|
206
243
|
// Windows 下 git 是 .exe;POSIX 下直接 PATH 找。
|
|
207
244
|
// 不传 shell,杜绝 cmd.exe 单/双引号兼容问题与注入风险。
|
|
208
|
-
windowsHide: true
|
|
245
|
+
windowsHide: true,
|
|
246
|
+
// 超时后 execFile 自动给子进程发 SIGTERM,callback 收到 error.killed=true
|
|
247
|
+
timeout
|
|
209
248
|
}, (error, stdout, stderr) => {
|
|
210
249
|
if (options.spinner) {
|
|
211
250
|
options.spinner.stop();
|
|
@@ -271,6 +310,15 @@ function execGitCommand(command, options = {}) {
|
|
|
271
310
|
console.log(chalk.dim(`📁 目录: ${cwd} | ⏰ 时间: ${currentTime}`));
|
|
272
311
|
}
|
|
273
312
|
if (error) {
|
|
313
|
+
// 检测是否超时:execFile 超时后 error.killed=true,signal='SIGTERM',
|
|
314
|
+
// error.code=null(被信号杀,无 exit code)。在 error 上加 isTimeout 标记
|
|
315
|
+
// 方便上层(路由 / GUI 前端)区分"超时"和"git 自己报错"做不同提示。
|
|
316
|
+
if (error.killed && (error.signal === 'SIGTERM' || /TIMEOUT|timeout/i.test(error.message || ''))) {
|
|
317
|
+
error.isTimeout = true
|
|
318
|
+
if (log) {
|
|
319
|
+
console.log(chalk.yellow(`⏰ git 命令超时 (${timeout}ms): ${head}`))
|
|
320
|
+
}
|
|
321
|
+
}
|
|
274
322
|
log && coloredLog(head, error, 'error')
|
|
275
323
|
// 错误情况也打印目录和时间
|
|
276
324
|
if (log) {
|
|
@@ -294,31 +342,136 @@ function execGitCommand(command, options = {}) {
|
|
|
294
342
|
|
|
295
343
|
/**
|
|
296
344
|
* 检查并尝试清理 Git 锁文件
|
|
345
|
+
*
|
|
346
|
+
* 安全策略:必须先验证锁是否真的过期(孤儿锁),才能 unlink。盲目删锁
|
|
347
|
+
* 会删掉别人正在写的锁 → `.git/index` 半写状态 → 仓库损坏。
|
|
348
|
+
*
|
|
349
|
+
* 判定顺序:
|
|
350
|
+
* 1. 读 .git/index.lock 内容,若能解析为 PID,调 process.kill(pid, 0) 探测
|
|
351
|
+
* - 探测成功(无错)→ 进程存活,锁仍被持有 → 跳过清理
|
|
352
|
+
* - ESRCH(无此进程)→ 锁是孤儿 → 进入清理
|
|
353
|
+
* - EPERM(进程存在但无权限)→ 锁被别人持有且我们是其他用户 → 跳过
|
|
354
|
+
* 2. 锁文件内容不是 PID(原生 git 通过 O_CREAT|O_EXCL 创建空文件作锁,
|
|
355
|
+
* 内容几乎总为空;此分支为兼容场景与未知第三方工具的 mtime 兜底):
|
|
356
|
+
* - force=false(默认):超过 5 分钟未更新视为陈旧,否则保守跳过
|
|
357
|
+
* - force=true:绕过 mtime 判断直接视为陈旧(用于破坏性入口,
|
|
358
|
+
* 如 reset --hard / discard-all-changes — 用户主动触发,
|
|
359
|
+
* 不该被一个刚崩出来的新孤儿锁挡住)
|
|
360
|
+
*
|
|
361
|
+
* 注:force=true 仅绕过 mtime,**仍走 PID liveness 校验**——
|
|
362
|
+
* 若锁由活跃进程持有(无论新旧)一律跳过,确保不会误删活跃锁。
|
|
363
|
+
*
|
|
364
|
+
* 注:阈值取 5min 而非 60s,是为大仓库的 git add -A / stash / gc 等长操作
|
|
365
|
+
* 留出空间 — 60s 阈值下用户在手抖触发清理时可能误删活跃锁,
|
|
366
|
+
* 导致 .git/index 半写损坏。5min 是 GitHub Desktop / Sourcetree 等
|
|
367
|
+
* GUI 客户端常用的陈旧判定阈值。
|
|
368
|
+
*
|
|
369
|
+
* @param {Object} [options]
|
|
370
|
+
* @param {boolean} [options.force=false] 是否绕过 mtime 兜底判断(破坏性入口用)。
|
|
371
|
+
* 注意:force 不影响 PID liveness 校验——活跃进程持有的锁永远不会被删。
|
|
297
372
|
* @returns {Promise<boolean>} 是否清理成功
|
|
298
373
|
*/
|
|
299
|
-
async function checkAndClearGitLock() {
|
|
374
|
+
async function checkAndClearGitLock({ force = false } = {}) {
|
|
375
|
+
// 锁文件"陈旧"阈值(毫秒):5min 是经验值。
|
|
376
|
+
// - 大仓库 git add -A / stash / gc 单次可能 > 60s,60s 阈值会误删活跃锁;
|
|
377
|
+
// - 5min 与 GitHub Desktop / Sourcetree 等 GUI 客户端的陈旧判定一致;
|
|
378
|
+
// - 只有真正崩溃/被 kill -9 留下的孤儿锁才会存在这么久。
|
|
379
|
+
// - force=true 时此阈值被绕过,但仍受 PID liveness 校验保护。
|
|
380
|
+
const STALE_LOCK_THRESHOLD_MS = 5 * 60_000;
|
|
300
381
|
try {
|
|
301
382
|
const cwd = getCwd();
|
|
302
383
|
let gitRoot;
|
|
303
384
|
try {
|
|
304
|
-
// 使用 execSync 快速获取 Git
|
|
305
|
-
const rootOutput = execSync('git rev-parse --show-toplevel', {
|
|
385
|
+
// 使用 execSync 快速获取 Git 根目录,加 5s 超时避免 rev-parse 卡死
|
|
386
|
+
const rootOutput = execSync('git rev-parse --show-toplevel', {
|
|
387
|
+
cwd,
|
|
388
|
+
encoding: 'utf-8',
|
|
389
|
+
timeout: 5000
|
|
390
|
+
});
|
|
306
391
|
gitRoot = rootOutput.trim();
|
|
307
392
|
} catch (e) {
|
|
308
393
|
gitRoot = cwd;
|
|
309
394
|
}
|
|
310
395
|
|
|
311
396
|
const lockFilePath = path.join(gitRoot, '.git', 'index.lock');
|
|
397
|
+
|
|
398
|
+
// 一次 IO 替代原来的 fs.access + fs.unlink 两步
|
|
399
|
+
let content;
|
|
312
400
|
try {
|
|
313
|
-
await fs.
|
|
314
|
-
// 如果文件存在,尝试删除它
|
|
315
|
-
await fs.unlink(lockFilePath);
|
|
316
|
-
console.log(chalk.green(`✅ 已清理 Git 锁文件: ${lockFilePath}`));
|
|
317
|
-
return true;
|
|
401
|
+
content = await fs.readFile(lockFilePath, 'utf-8');
|
|
318
402
|
} catch (e) {
|
|
319
|
-
//
|
|
320
|
-
|
|
403
|
+
if (e.code === 'ENOENT') return false; // 锁文件不存在,无需清理
|
|
404
|
+
throw e;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// 判定锁是否过期
|
|
408
|
+
const trimmed = (content || '').trim();
|
|
409
|
+
// 原生 git 通过 O_CREAT|O_EXCL 创建空文件作锁,内容几乎总为空,
|
|
410
|
+
// 不会往 .git/index.lock 写 PID。此 PID 分支为兼容某些会写 PID 的
|
|
411
|
+
// 第三方工具(如部分 GUI 客户端 / 自定义钩子),并非常规路径。
|
|
412
|
+
const isPidFormat = /^\d+$/.test(trimmed);
|
|
413
|
+
let isStale = false;
|
|
414
|
+
|
|
415
|
+
if (isPidFormat) {
|
|
416
|
+
const pid = parseInt(trimmed, 10);
|
|
417
|
+
if (Number.isInteger(pid) && pid > 0) {
|
|
418
|
+
try {
|
|
419
|
+
// signal 0 不真发信号,只探测进程是否存在
|
|
420
|
+
process.kill(pid, 0);
|
|
421
|
+
// 无错 → 进程存活,锁还活着
|
|
422
|
+
console.log(chalk.yellow(`⚠️ 检测到 Git 锁由活跃进程持有 (PID=${pid}),跳过清理`));
|
|
423
|
+
return false;
|
|
424
|
+
} catch (e) {
|
|
425
|
+
if (e.code === 'ESRCH') {
|
|
426
|
+
// 进程已死,锁是孤儿
|
|
427
|
+
isStale = true;
|
|
428
|
+
} else if (e.code === 'EPERM') {
|
|
429
|
+
// 进程存在但无权限(其他用户的进程),不能删
|
|
430
|
+
console.log(chalk.yellow(`⚠️ 检测到 Git 锁由无权限访问的进程持有,跳过清理`));
|
|
431
|
+
return false;
|
|
432
|
+
} else {
|
|
433
|
+
// 其他探测错误,保守不删
|
|
434
|
+
console.warn(chalk.yellow(`⚠️ 探测 PID liveness 失败: ${e.message},跳过清理`));
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
// 锁文件内容不是 PID — 原生 git 用 O_CREAT|O_EXCL 创建空文件,
|
|
441
|
+
// 内容为空,用 mtime 兜底
|
|
442
|
+
if (force) {
|
|
443
|
+
// 破坏性入口(reset --hard / discard-all-changes 等):用户主动
|
|
444
|
+
// 触发,不应被刚崩出来的新孤儿锁挡住。PID liveness 已在上面通过
|
|
445
|
+
// (走到这里说明锁不是 PID 格式或 PID 已死),直接视为陈旧清理。
|
|
446
|
+
// 仍打印 mtime 便于排查。
|
|
447
|
+
try {
|
|
448
|
+
const stat = await fs.stat(lockFilePath);
|
|
449
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
450
|
+
console.log(chalk.gray(`ℹ️ force=true,绕过 mtime 判断清理 Git 锁(年龄 ${Math.round(ageMs / 1000)}s)`));
|
|
451
|
+
} catch (_) {}
|
|
452
|
+
isStale = true;
|
|
453
|
+
} else {
|
|
454
|
+
try {
|
|
455
|
+
const stat = await fs.stat(lockFilePath);
|
|
456
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
457
|
+
if (ageMs > STALE_LOCK_THRESHOLD_MS) {
|
|
458
|
+
isStale = true;
|
|
459
|
+
} else {
|
|
460
|
+
console.log(chalk.yellow(`⚠️ Git 锁文件较新(${Math.round(ageMs / 1000)}s),保守跳过清理`));
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
} catch (_) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
321
467
|
}
|
|
468
|
+
|
|
469
|
+
if (!isStale) return false;
|
|
470
|
+
|
|
471
|
+
// 锁是孤儿,清理
|
|
472
|
+
await fs.unlink(lockFilePath);
|
|
473
|
+
console.log(chalk.green(`✅ 已清理过期 Git 锁文件: ${lockFilePath}`));
|
|
474
|
+
return true;
|
|
322
475
|
} catch (error) {
|
|
323
476
|
console.error(chalk.red('清理 Git 锁文件失败:'), error.message);
|
|
324
477
|
return false;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-CMxvf4Kt.js";import{B as e,Cn as t,Fn as n,Jn as r,Kn as i,Ln as a,Lt as o,Pn as s,Pt as c,Sn as l,Tn as u,Yn as d,_n as f,a as p,ar as m,bt as h,dr as g,fr as _,gn as v,hn as y,hr as b,ht as x,i as S,ln as ee,lt as C,mn as w,o as te,pt as ne,un as re,vn as T,vt as E,xn as D,zn as ie}from"./element-plus-B6Owr5jY.js";import{d as ae,o as oe,u as O}from"./vendor-BuiZXcjj.js";import{n as k,t as A}from"./_plugin-vue_export-helper-BMXWW0fG.js";import{n as se,t as j}from"./configStore-DHCrsR6z.js";import{n as M,r as N}from"./flow-mindmap-CAkGY2ir.js";var P=`zen-mindmap-dir`,F=``,I=O(`mindmap`,()=>{let e=m(l()),t=m([]),n=m(null),r=m(!1),i=m(!1),a=m(null),o=m(``),s=w(()=>n.value!==null),c=w(()=>t.value.length);function l(){try{return localStorage.getItem(P)||F}catch{return F}}function u(e){try{e?localStorage.setItem(P,e):localStorage.removeItem(P)}catch{}}async function d(n){let r=n??e.value;if(!r){a.value=null,t.value=[];return}i.value=!0,a.value=null;try{let n=await(await fetch(`/api/mindmap/list?dir=${encodeURIComponent(r)}`)).json();n.success?(e.value=n.dir,t.value=n.files||[],u(n.dir)):(a.value=n.error||`读取目录失败`,t.value=[])}catch(e){a.value=e?.message||String(e),t.value=[]}finally{i.value=!1}}async function f(e){await d(e)}async function p(e,t=!1){if(!t&&r.value&&n.value)throw Error(`UNSAVED_CHANGES`);i.value=!0,a.value=null;try{let t=await(await fetch(`/api/mindmap/read?path=${encodeURIComponent(e)}`)).json();t.success?(n.value={path:t.path,title:t.title,content:t.content,mtime:t.mtime},o.value=t.content,r.value=!1):a.value=t.error||`读取文件失败`}catch(e){a.value=e?.message||String(e)}finally{i.value=!1}}async function h(t,s=!1){if(!e.value)throw Error(`未选择目录`);if(!s&&r.value&&n.value)throw Error(`UNSAVED_CHANGES`);i.value=!0,a.value=null;try{let i=await(await fetch(`/api/mindmap/create`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({dir:e.value,name:t})})).json();if(i.success)n.value={path:i.path,title:i.title,content:i.content,mtime:i.mtime},o.value=i.content,r.value=!1,await d();else throw a.value=i.error||`新建失败`,Error(i.error||`新建失败`)}catch(e){throw e.message===`UNSAVED_CHANGES`||(a.value=e?.message||String(e)),e}finally{i.value=!1}}async function g(e){if(!n.value)throw Error(`没有打开的文件`);i.value=!0,a.value=null;try{let t=await(await fetch(`/api/mindmap/save`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:n.value.path,content:e})})).json();if(t.success)return n.value&&(n.value.mtime=t.mtime),o.value=e,r.value=!1,await d(),t;throw a.value=t.error||`保存失败`,Error(t.error||`保存失败`)}catch(e){throw a.value=e?.message||String(e),e}finally{i.value=!1}}async function _(e){let t=await(await fetch(`/api/mindmap/delete`,{method:`DELETE`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e})})).json();if(!t.success)throw Error(t.error||`删除失败`);return n.value&&n.value.path===e&&(n.value=null,r.value=!1,o.value=``),await d(),t}async function v(e,t){let r=await(await fetch(`/api/mindmap/rename`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e,newName:t})})).json();if(!r.success)throw Error(r.error||`重命名失败`);return n.value&&e===n.value.path&&(n.value={...n.value,path:r.path,title:t}),await d(),r}function y(){n.value&&(r.value=!0)}function b(){n.value=null,r.value=!1,o.value=``}return{currentDir:e,files:t,current:n,dirty:r,loading:i,error:a,hasCurrent:s,filesCount:c,listFiles:d,setDir:f,openFile:p,createFile:h,saveCurrent:g,deleteFile:_,renameFile:v,markDirty:y,closeCurrent:b}}),L={class:`mindmap-view`},R={class:`mm-toolbar`},z={class:`mm-toolbar-left`},B=[`title`],V={key:1,class:`mm-dir-empty`},H={class:`mm-toolbar-right`},U={key:0,class:`mm-status`},W=[`title`],G={key:0,class:`mm-status-dirty`},K={class:`mm-body`},ce={class:`mm-sidebar`},le={class:`mm-sidebar-header`},ue={class:`mm-sidebar-title`},de={class:`mm-sidebar-list`},fe={key:0,class:`mm-sidebar-error`},pe={key:1,class:`mm-sidebar-empty`},me={key:2,class:`mm-sidebar-empty`},he=[`onClick`],ge={class:`mm-file-info`},_e=[`title`],ve={class:`mm-file-meta`},ye={class:`mm-editor`},be={key:0,class:`mm-editor-empty`},xe={class:`mm-editor-empty-title`},Se={class:`mm-editor-empty-hint`},q=A(u({__name:`MindmapView`,setup(u){let O=I(),A=j(),{currentLocale:P}=ae(se()),F=w(()=>{let e=A.theme;return e===`dark`?!0:e===`light`?!1:window.matchMedia?.(`(prefers-color-scheme: dark)`).matches??!1}),q=m(null),J=m(!1);function Ce(){J.value=!0}function we(e){J.value=!1,e&&e.length>0&&O.setDir(e[0])}async function Te(){if(!O.currentDir){p.warning(k(`@MINDMAP:请先选择目录`));return}if(!await X())return;let e=``;try{e=(await S.prompt(k(`@MINDMAP:输入文件名提示`),k(`@MINDMAP:新建思维导图`),{confirmButtonText:k(`@MINDMAP:新建`),cancelButtonText:k(`@MINDMAP:取消`),inputPlaceholder:k(`@MINDMAP:文件名占位`),inputValidator:e=>!e||!e.trim()?k(`@MINDMAP:名称不能为空`):/[\\/:*?"<>|]/.test(e)?k(`@MINDMAP:名称含非法字符`):!0})).value.trim()}catch{return}try{await O.createFile(e,!0),p.success(k(`@MINDMAP:已新建并打开`))}catch(e){p.error(e?.message||k(`@MINDMAP:新建失败`))}}async function Y(){if(O.current)try{let e=q.value?.exportData();if(!e){p.warning(k(`@MINDMAP:无法获取数据`));return}await O.saveCurrent(e),p.success(k(`@MINDMAP:已保存`))}catch(e){p.error(e?.message||k(`@MINDMAP:保存失败`))}}async function X(){if(!O.dirty||!O.current)return!0;try{await S.confirm(k(`@MINDMAP:未保存提示`).replace(`{title}`,O.current.title),k(`@MINDMAP:未保存的改动`),{confirmButtonText:k(`@MINDMAP:保存`),cancelButtonText:k(`@MINDMAP:丢弃`),distinguishCancelAndClose:!0,type:`warning`});let e=q.value?.exportData();return e&&(await O.saveCurrent(e),p.success(k(`@MINDMAP:已保存`))),!0}catch(e){return e===`cancel`}}async function Z(e){if(!(O.current&&O.current.path===e)&&await X())try{await O.openFile(e,!0)}catch(e){p.error(e?.message||k(`@MINDMAP:打开失败`))}}async function Ee(e){try{await S.confirm(k(`@MINDMAP:确认删除提示`).replace(`{title}`,e.title),k(`@MINDMAP:删除`),{confirmButtonText:k(`@MINDMAP:删除`),cancelButtonText:k(`@MINDMAP:取消`),type:`warning`})}catch{return}try{await O.deleteFile(e.path),p.success(k(`@MINDMAP:已删除`))}catch(e){p.error(e?.message||k(`@MINDMAP:删除失败`))}}async function De(e){let t=``;try{t=(await S.prompt(k(`@MINDMAP:输入新名称提示`),k(`@MINDMAP:重命名`),{confirmButtonText:k(`@MINDMAP:确定`),cancelButtonText:k(`@MINDMAP:取消`),inputValue:e.title,inputValidator:e=>!e||!e.trim()?k(`@MINDMAP:名称不能为空`):/[\\/:*?"<>|]/.test(e)?k(`@MINDMAP:名称含非法字符`):!0})).value.trim()}catch{return}try{await O.renameFile(e.path,t),p.success(k(`@MINDMAP:已重命名`))}catch(e){p.error(e?.message||k(`@MINDMAP:重命名失败`))}}function Oe(e){O.markDirty()}async function ke(){if(!O.currentDir){p.warning(k(`@MINDMAP:请先选择目录`));return}let e=``,t=``;try{e=(await S.prompt(k(`@MINDMAP:粘贴Markdown提示`),k(`@MINDMAP:从Markdown导入`),{confirmButtonText:k(`@MINDMAP:导入`),cancelButtonText:k(`@MINDMAP:取消`),inputType:`textarea`,inputPlaceholder:k(`@MINDMAP:Markdown占位`),inputValidator:e=>!e||!e.trim()?k(`@MINDMAP:内容不能为空`):!0})).value}catch{return}try{t=(await S.prompt(k(`@MINDMAP:输入文件名提示`),k(`@MINDMAP:文件名`),{confirmButtonText:k(`@MINDMAP:新建`),cancelButtonText:k(`@MINDMAP:取消`),inputValidator:e=>!e||!e.trim()?k(`@MINDMAP:名称不能为空`):/[\\/:*?"<>|]/.test(e)?k(`@MINDMAP:名称含非法字符`):!0})).value.trim()}catch{return}try{await O.createFile(t,!0);let n=N(e,t),r=JSON.stringify(n,null,2);await O.saveCurrent(r),p.success(k(`@MINDMAP:已导入`))}catch(e){p.error(e?.message||k(`@MINDMAP:导入失败`))}}async function Ae(){if(q.value)try{let e=q.value.getMarkdown();await navigator.clipboard.writeText(e),p.success(k(`@MINDMAP:已复制Markdown`))}catch(e){p.error(e?.message||k(`@MINDMAP:复制失败`))}}function Q(e){(e.ctrlKey||e.metaKey)&&e.key===`s`&&(e.preventDefault(),O.current&&O.dirty&&Y())}let $=m(0);i(()=>O.current?.path,()=>{$.value++}),n(async()=>{window.addEventListener(`keydown`,Q),O.currentDir&&await O.listFiles()}),s(()=>{window.removeEventListener(`keydown`,Q)});function je(e){if(!e)return`-`;let t=new Date(e),n=Date.now()-e;return n<6e4?k(`@MINDMAP:刚刚`):n<36e5?`${Math.floor(n/6e4)}${k(`@MINDMAP:分钟前`)}`:n<864e5?`${Math.floor(n/36e5)}${k(`@MINDMAP:小时前`)}`:`${t.getMonth()+1}/${t.getDate()} ${String(t.getHours()).padStart(2,`0`)}:${String(t.getMinutes()).padStart(2,`0`)}`}function Me(e){return e?e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(1)} MB`:`0 B`}return(n,i)=>{let s=e,u=te;return a(),T(`div`,L,[y(`div`,R,[y(`div`,z,[t(s,{size:`small`,icon:g(h),onClick:Ce},{default:r(()=>[l(b(g(k)(`@MINDMAP:选择目录`)),1)]),_:1},8,[`icon`]),g(O).currentDir?(a(),T(`span`,{key:0,class:`mm-dir-path`,title:g(O).currentDir},b(g(O).currentDir),9,B)):(a(),T(`span`,V,b(g(k)(`@MINDMAP:未选择目录`)),1))]),y(`div`,H,[t(s,{size:`small`,icon:g(c),disabled:!g(O).currentDir,onClick:Te},{default:r(()=>[l(b(g(k)(`@MINDMAP:新建`)),1)]),_:1},8,[`icon`,`disabled`]),t(s,{size:`small`,icon:g(ne),disabled:!g(O).currentDir,onClick:ke},{default:r(()=>[l(b(g(k)(`@MINDMAP:从MD导入`)),1)]),_:1},8,[`icon`,`disabled`]),t(s,{size:`small`,icon:g(x),disabled:!g(O).current,onClick:Ae},{default:r(()=>[l(b(g(k)(`@MINDMAP:复制MD`)),1)]),_:1},8,[`icon`,`disabled`]),t(s,{size:`small`,type:`primary`,icon:g(E),disabled:!g(O).current||!g(O).dirty,loading:g(O).loading,onClick:Y},{default:r(()=>[l(b(g(k)(`@MINDMAP:保存`)),1)]),_:1},8,[`icon`,`disabled`,`loading`]),g(O).current?(a(),T(`span`,U,[y(`span`,{class:`mm-status-title`,title:g(O).current.title},b(g(O).current.title),9,W),g(O).dirty?(a(),T(`span`,G,`●`)):f(``,!0)])):f(``,!0)])]),y(`div`,K,[y(`div`,ce,[y(`div`,le,[y(`span`,ue,b(g(k)(`@MINDMAP:文件列表`)),1),t(s,{size:`small`,text:``,icon:g(o),loading:g(O).loading,onClick:i[0]||=e=>g(O).listFiles(),disabled:!g(O).currentDir},null,8,[`icon`,`loading`,`disabled`])]),d((a(),T(`div`,de,[g(O).error&&!g(O).loading?(a(),T(`div`,fe,b(g(O).error),1)):g(O).currentDir?g(O).files.length===0?(a(),T(`div`,me,b(g(k)(`@MINDMAP:暂无思维导图`)),1)):(a(!0),T(re,{key:3},ie(g(O).files,e=>(a(),T(`div`,{key:e.path,class:_([`mm-file-item`,{active:g(O).current?.path===e.path}]),onClick:t=>Z(e.path)},[i[3]||=D(`<div class="mm-file-icon" data-v-38169157><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-38169157><circle cx="12" cy="12" r="2" data-v-38169157></circle><circle cx="5" cy="5" r="2" data-v-38169157></circle><circle cx="19" cy="5" r="2" data-v-38169157></circle><circle cx="5" cy="19" r="2" data-v-38169157></circle><circle cx="19" cy="19" r="2" data-v-38169157></circle><line x1="10.5" y1="10.5" x2="6.5" y2="6.5" data-v-38169157></line><line x1="13.5" y1="10.5" x2="17.5" y2="6.5" data-v-38169157></line><line x1="10.5" y1="13.5" x2="6.5" y2="17.5" data-v-38169157></line><line x1="13.5" y1="13.5" x2="17.5" y2="17.5" data-v-38169157></line></svg></div>`,1),y(`div`,ge,[y(`div`,{class:`mm-file-title`,title:e.title},b(e.title),9,_e),y(`div`,ve,b(je(e.mtime))+` · `+b(Me(e.size)),1)]),y(`div`,{class:`mm-file-actions`,onClick:i[1]||=ee(()=>{},[`stop`])},[t(s,{size:`small`,text:``,icon:g(x),onClick:t=>De(e),title:g(k)(`@MINDMAP:重命名`)},null,8,[`icon`,`onClick`,`title`]),t(s,{size:`small`,text:``,type:`danger`,icon:g(C),onClick:t=>Ee(e),title:g(k)(`@MINDMAP:删除`)},null,8,[`icon`,`onClick`,`title`])])],10,he))),128)):(a(),T(`div`,pe,b(g(k)(`@MINDMAP:请先选择目录`)),1))])),[[u,g(O).loading]])]),y(`div`,ye,[g(O).current?(a(),v(g(M),{key:$.value,ref_key:`mmRef`,ref:q,data:JSON.parse(g(O).current.content),onChange:Oe},null,8,[`data`])):(a(),T(`div`,be,[i[4]||=D(`<svg viewBox="0 0 24 24" width="56" height="56" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" data-v-38169157><circle cx="12" cy="12" r="2" data-v-38169157></circle><circle cx="5" cy="5" r="2" data-v-38169157></circle><circle cx="19" cy="5" r="2" data-v-38169157></circle><circle cx="5" cy="19" r="2" data-v-38169157></circle><circle cx="19" cy="19" r="2" data-v-38169157></circle><line x1="10.5" y1="10.5" x2="6.5" y2="6.5" data-v-38169157></line><line x1="13.5" y1="10.5" x2="17.5" y2="6.5" data-v-38169157></line><line x1="10.5" y1="13.5" x2="6.5" y2="17.5" data-v-38169157></line><line x1="13.5" y1="13.5" x2="17.5" y2="17.5" data-v-38169157></line></svg>`,1),y(`p`,xe,b(g(k)(`@MINDMAP:思维导图编辑器`)),1),y(`p`,Se,b(g(O).currentDir?g(k)(`@MINDMAP:从左侧选择或新建`):g(k)(`@MINDMAP:先选择目录提示`)),1)]))])]),t(g(oe),{visible:J.value,mode:`directory`,theme:F.value?`dark`:`light`,locale:g(P),onClose:i[2]||=e=>J.value=!1,onConfirm:we},null,8,[`visible`,`theme`,`locale`])])}}}),[[`__scopeId`,`data-v-38169157`]]);export{q as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.mindmap-view[data-v-38169157]{background:var(--bg-page);flex-direction:column;height:100%;min-height:0;display:flex;overflow:hidden}.mm-toolbar[data-v-38169157]{background:var(--bg-container);border-bottom:1px solid var(--border-color);flex-shrink:0;justify-content:space-between;align-items:center;gap:12px;padding:8px 14px;display:flex}.mm-toolbar-left[data-v-38169157],.mm-toolbar-right[data-v-38169157]{align-items:center;gap:8px;min-width:0;display:flex}.mm-toolbar-right[data-v-38169157]{flex-shrink:0}.mm-dir-path[data-v-38169157]{color:var(--text-secondary);font-size:12px;font-family:var(--font-mono,monospace);white-space:nowrap;text-overflow:ellipsis;background:var(--bg-subtle);border-radius:var(--radius-sm);max-width:320px;padding:2px 8px;overflow:hidden}.mm-dir-empty[data-v-38169157]{color:var(--text-tertiary);font-size:12px;font-style:italic}.mm-status[data-v-38169157]{color:var(--text-secondary);align-items:center;gap:4px;margin-left:4px;font-size:12px;display:flex}.mm-status-title[data-v-38169157]{white-space:nowrap;text-overflow:ellipsis;max-width:160px;font-weight:500;overflow:hidden}.mm-status-dirty[data-v-38169157]{color:var(--color-warning);font-size:14px;line-height:1}.mm-body[data-v-38169157]{flex:1;min-height:0;display:flex;overflow:hidden}.mm-sidebar[data-v-38169157]{background:var(--bg-container);border-right:1px solid var(--border-color);flex-direction:column;flex-shrink:0;width:240px;display:flex}.mm-sidebar-header[data-v-38169157]{border-bottom:1px solid var(--border-color);background:var(--bg-panel);justify-content:space-between;align-items:center;padding:8px 12px;display:flex}.mm-sidebar-title[data-v-38169157]{color:var(--text-secondary);letter-spacing:.5px;text-transform:uppercase;font-size:12px;font-weight:600}.mm-sidebar-list[data-v-38169157]{flex:1;min-height:0;padding:4px;overflow-y:auto}.mm-sidebar-empty[data-v-38169157],.mm-sidebar-error[data-v-38169157]{text-align:center;color:var(--text-tertiary);padding:24px 12px;font-size:12px}.mm-sidebar-error[data-v-38169157]{color:var(--color-danger)}.mm-file-item[data-v-38169157]{border-radius:var(--radius-md);cursor:pointer;align-items:center;gap:8px;padding:6px 8px;transition:background .15s;display:flex;position:relative}.mm-file-item[data-v-38169157]:hover{background:var(--bg-hover)}.mm-file-item.active[data-v-38169157]{background:color-mix(in srgb, var(--color-primary) 12%, transparent)}.mm-file-item.active .mm-file-title[data-v-38169157]{color:var(--color-primary);font-weight:600}.mm-file-icon[data-v-38169157]{color:var(--text-tertiary);flex-shrink:0;align-items:center;display:flex}.mm-file-item.active .mm-file-icon[data-v-38169157]{color:var(--color-primary)}.mm-file-info[data-v-38169157]{flex-direction:column;flex:1;gap:1px;min-width:0;display:flex}.mm-file-title[data-v-38169157]{color:var(--text-primary);white-space:nowrap;text-overflow:ellipsis;font-size:13px;overflow:hidden}.mm-file-meta[data-v-38169157]{color:var(--text-tertiary);white-space:nowrap;text-overflow:ellipsis;font-size:10.5px;overflow:hidden}.mm-file-actions[data-v-38169157]{opacity:0;align-items:center;gap:2px;transition:opacity .15s;display:flex}.mm-file-item:hover .mm-file-actions[data-v-38169157]{opacity:1}.mm-editor[data-v-38169157]{background:var(--bg-container);flex:1;min-width:0;min-height:0;position:relative}.mm-editor[data-v-38169157] .zm-mindmap,.mm-editor[data-v-38169157]>*{width:100%;height:100%}.mm-editor-empty[data-v-38169157]{color:var(--text-tertiary);flex-direction:column;justify-content:center;align-items:center;gap:8px;display:flex;position:absolute;inset:0}.mm-editor-empty svg[data-v-38169157]{opacity:.4}.mm-editor-empty-title[data-v-38169157]{color:var(--text-secondary);margin:8px 0 0;font-size:16px;font-weight:600}.mm-editor-empty-hint[data-v-38169157]{color:var(--text-tertiary);margin:0;font-size:13px}@media (width<=900px){.mm-sidebar[data-v-38169157]{width:180px}.mm-dir-path[data-v-38169157]{max-width:160px}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-CMxvf4Kt.js";import{Kn as e,a as t,ar as n,mn as r,n as i,t as a}from"./element-plus-B6Owr5jY.js";import{u as o}from"./vendor-BuiZXcjj.js";import{a as s,n as c,r as l}from"./_plugin-vue_export-helper-BMXWW0fG.js";var u={"zh-CN":i,"en-US":a};function d(e){return u[e]||i}var f=o(`locale`,()=>{let e=n(l()),t=r(()=>d(e.value));async function i(t){s(t),e.value=t;let n=p();n.locale!==t&&await n.saveGeneralSettings({locale:t})}return{currentLocale:e,elementPlusLocale:t,changeLocale:i}}),p=o(`config`,()=>{let i=n(``),a=n([]),o=n([]),l=n([]),u=n([]),d=n([]),p=n([]),m=n([]),h=n([]),g=n(!1),_=n({enabled:!1,type:`command`,refId:``}),v=n(!1),y=n(!1),b=n(``),x=n(``),S=n(``),C=n(!0),w=n(!1),T=n(!0),E=n(!1),D=n(!1),O=n(!0),k=n(`light`),A=n(`zh-CN`),j=n([]),M={layout:{leftRatio:.25,midRatio:.375,rightRatio:.375,topRatio:.5},fileListViewMode:`list`,fileDiffSplitPercent:35,commandConsole:{expanded:!0,useTerminal:!0,showTerminalSessions:!0,splitPercent:25},editorAutoSave:!1},N=n(JSON.parse(JSON.stringify(M))),P=n(!1);function F(e){S.value=e||``}function I(e){let t=document.documentElement;e===`dark`?t.setAttribute(`data-theme`,`dark`):e===`light`?t.removeAttribute(`data-theme`):window.matchMedia(`(prefers-color-scheme: dark)`).matches?t.setAttribute(`data-theme`,`dark`):t.removeAttribute(`data-theme`)}function L(){let e=document.documentElement.getAttribute(`data-theme`)===`dark`?`light`:`dark`;return k.value=e,I(e),$({theme:e}).catch(e=>{console.error(`保存主题切换结果失败:`,e)}),e}I(k.value);let R=r(()=>({defaultCommitMessage:i.value,descriptionTemplates:a.value,scopeTemplates:o.value,messageTemplates:l.value,commandTemplates:u.value,lockedFiles:d.value,customCommands:p.value,orchestrations:m.value,startupItems:h.value,startupAutoRun:g.value,afterQuickPushAction:_.value,currentDirectory:S.value})),ee=r(()=>!!(b.value&&b.value.trim()));async function z(){try{let e=await fetch(`/api/config/open-file`,{method:`POST`,headers:{"Content-Type":`application/json`}}),n=await e.json().catch(()=>({}));return e.ok&&n?.success?(t.success(c(`@D50BB:已打开系统配置文件`)),!0):(t.error(`${c(`@D50BB:打开系统配置文件失败: `)}${n?.error||``}`),!1)}catch(e){return t.error(`${c(`@D50BB:打开系统配置文件失败: `)}${e.message}`),!1}}async function B(e=!1){if(!e&&y.value&&!v.value)return console.log(c(`@D50BB:使用缓存的配置信息`)),R.value;v.value=!0;try{v.value=!0,console.log(c(`@D50BB:加载配置信息...`));let e=await fetch(`/api/config/getConfig`),n=await e.json().catch(()=>({}));if(!e.ok||n&&n.success===!1){let e=String(n?.error||c(`@D50BB:加载配置失败`)),r=String(n?.configPath||``),i=r?`\n${c(`@D50BB:配置文件`)}: ${r}`:``;return t.error(`${c(`@D50BB:加载配置失败: `)}${e}${i}`),b.value=e,x.value=r,null}if(b.value=``,x.value=``,i.value=n.defaultCommitMessage||``,a.value=n.descriptionTemplates||[],o.value=n.scopeTemplates||[],l.value=n.messageTemplates||[],u.value=n.commandTemplates||[],d.value=n.lockedFiles||[],p.value=n.customCommands||[],m.value=n.orchestrations||[],h.value=(n.startupItems||[]).map(e=>({id:String(e?.id??``),type:e?.type===`workflow`?`workflow`:`command`,refId:String(e?.refId??e?.commandId??``),createdAt:Number(e?.createdAt??Date.now()),enabled:typeof e?.enabled==`boolean`?e.enabled:!0})).filter(e=>e.id&&e.refId),g.value=n.startupAutoRun||!1,_.value={enabled:!!n?.afterQuickPushAction?.enabled,type:n?.afterQuickPushAction?.type===`workflow`?`workflow`:`command`,refId:String(n?.afterQuickPushAction?.refId||``).trim()},n.isStandardCommit!==void 0&&(T.value=!!n.isStandardCommit),n.skipHooks!==void 0&&(E.value=!!n.skipHooks),n.autoQuickPushOnEnter!==void 0&&(D.value=!!n.autoQuickPushOnEnter),n.autoSetDefaultMessage!==void 0&&(w.value=!!n.autoSetDefaultMessage),n.autoClosePushModal!==void 0&&(C.value=!!n.autoClosePushModal),n.pullBeforePush!==void 0&&(O.value=!!n.pullBeforePush),Array.isArray(n.models)&&(j.value=n.models),n.theme&&[`light`,`dark`,`auto`].includes(n.theme)&&(k.value=n.theme,I(n.theme)),n.locale&&[`zh-CN`,`en-US`].includes(n.locale)){A.value=n.locale,s(n.locale);let e=f();e.currentLocale=n.locale}if(n.currentDirectory&&(S.value=n.currentDirectory),n.ui==null){let e=e=>{try{return localStorage.getItem(e)}catch{return null}},t={},n=e(`zen-gitsync-layout-left-ratio`),r=e(`zen-gitsync-layout-mid-ratio`),i=e(`zen-gitsync-layout-right-ratio`),a=e(`zen-gitsync-layout-top-ratio`);if(n||r||i||a){let e=(e,t)=>{let n=e==null?NaN:parseFloat(e);return Number.isFinite(n)?n:t};t.layout={leftRatio:e(n,M.layout.leftRatio),midRatio:e(r,M.layout.midRatio),rightRatio:e(i,M.layout.rightRatio),topRatio:e(a,M.layout.topRatio)}}let o=e(`zen-gitsync-file-list-view-mode`);(o===`list`||o===`tree`)&&(t.fileListViewMode=o);let s=e(`zen-gitsync-filediff-ratio`)??e(`fileDiff.splitPercent`);if(s!=null){let e=parseFloat(s);Number.isFinite(e)&&(t.fileDiffSplitPercent=Math.min(85,Math.max(15,e)))}let c=e(`useTerminal`),l=e(`isConsoleExpanded`),u=e(`showTerminalSessions`),d=e(`zen-gitsync-commandconsole-ratio`);if(c!==null||l!==null||u!==null||d!=null){let e=d==null?NaN:parseFloat(d);t.commandConsole={useTerminal:c!==`false`,expanded:l!==`false`,showTerminalSessions:u==null?!0:u===`true`,splitPercent:Number.isFinite(e)?Math.min(85,Math.max(15,e)):M.commandConsole.splitPercent}}let f=e(`zen-gitsync-editor-auto-save`);f!=null&&(t.editorAutoSave=f===`true`),Object.keys(t).length>0&&(N.value={...N.value,...t}),Object.keys(t).length>0&&G(t,{immediate:!0}).catch(e=>{console.error(`迁移 UI 设置到文件失败:`,e)});try{[`zen-gitsync-theme`,`zen-gitsync-locale`,`zen-gitsync-editor-auto-save`,`zen-gitsync-file-list-view-mode`,`zen-gitsync-filediff-ratio`,`fileDiff.splitPercent`,`isConsoleExpanded`,`useTerminal`,`showTerminalSessions`,`zen-gitsync-commandconsole-ratio`,`zen-gitsync-layout-left-ratio`,`zen-gitsync-layout-mid-ratio`,`zen-gitsync-layout-right-ratio`,`zen-gitsync-layout-top-ratio`,`locale`].forEach(e=>localStorage.removeItem(e))}catch{}}else N.value={layout:{...M.layout,...n.ui.layout||{}},fileListViewMode:n.ui.fileListViewMode===`tree`?`tree`:`list`,fileDiffSplitPercent:Number.isFinite(Number(n.ui.fileDiffSplitPercent))?Math.min(85,Math.max(15,Number(n.ui.fileDiffSplitPercent))):M.fileDiffSplitPercent,commandConsole:{expanded:typeof n.ui.commandConsole?.expanded==`boolean`?n.ui.commandConsole.expanded:M.commandConsole.expanded,useTerminal:typeof n.ui.commandConsole?.useTerminal==`boolean`?n.ui.commandConsole.useTerminal:M.commandConsole.useTerminal,showTerminalSessions:typeof n.ui.commandConsole?.showTerminalSessions==`boolean`?n.ui.commandConsole.showTerminalSessions:M.commandConsole.showTerminalSessions,splitPercent:Number.isFinite(Number(n.ui.commandConsole?.splitPercent))?Math.min(85,Math.max(15,Number(n.ui.commandConsole.splitPercent))):M.commandConsole.splitPercent},editorAutoSave:typeof n.ui.editorAutoSave==`boolean`?n.ui.editorAutoSave:M.editorAutoSave};return P.value=!0,y.value=!0,console.log(c(`@D50BB:配置信息加载完成`)),R.value}catch(e){return console.error(`加载配置失败:`,e),t.error(`${c(`@D50BB:加载配置失败: `)}${e.message}`),b.value=e.message,null}finally{v.value=!1}}async function V(){try{await fetch(`/api/config/save-commit-settings`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({isStandardCommit:T.value,skipHooks:E.value,autoQuickPushOnEnter:D.value,autoSetDefaultMessage:w.value,autoClosePushModal:C.value,pullBeforePush:O.value})})}catch(e){console.error(`保存提交设置失败:`,e)}}e([T,E,D,w,C,O],()=>{y.value&&V()});let H=null,U={};async function W(){if(H&&=(clearTimeout(H),null),Object.keys(U).length===0)return;let e=U;U={};try{await fetch(`/api/config/save-ui-settings`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})}catch(e){console.error(`保存 UI 设置失败:`,e)}}async function G(e,t={}){if(P.value){if(t.immediate){Object.assign(U,e),await W();return}Object.assign(U,e),H&&clearTimeout(H),H=setTimeout(()=>{W()},300)}}async function K(){return N.value.layout={...M.layout},await G({layout:N.value.layout},{immediate:!0}),N.value.layout}e(()=>N.value.fileListViewMode,e=>{P.value&&G({fileListViewMode:e})}),e(()=>N.value.fileDiffSplitPercent,e=>{P.value&&G({fileDiffSplitPercent:e})}),e(()=>N.value.editorAutoSave,e=>{P.value&&G({editorAutoSave:e})}),e(()=>N.value.layout,e=>{P.value&&G({layout:e})},{deep:!0}),e(()=>N.value.commandConsole,e=>{P.value&&G({commandConsole:e})},{deep:!0}),typeof window<`u`&&window.addEventListener(`beforeunload`,()=>{W()});async function q(e){try{let n=await(await fetch(`/api/config/saveDefaultMessage`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({defaultCommitMessage:e})})).json();return n.success?(i.value=e,t.success(c(`@D50BB:默认提交信息已保存`)),!0):(t.error(`${c(`@D50BB:保存失败: `)}${n.error}`),!1)}catch(e){return t.error(`${c(`@D50BB:保存失败: `)}${e.message}`),!1}}async function J(e){try{let n=await(await fetch(`/api/config/save-after-quick-push-action`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({afterQuickPushAction:{enabled:!!e?.enabled,type:e?.type===`workflow`?`workflow`:`command`,refId:String(e?.refId||``).trim()}})})).json();return n.success?(_.value={enabled:!!e?.enabled,type:e?.type===`workflow`?`workflow`:`command`,refId:String(e?.refId||``).trim()},!0):(t.error(`保存提交后启动项失败: ${n.error}`),!1)}catch(e){return t.error(`保存提交后启动项失败: ${e.message}`),!1}}async function Y(e,n){try{let r=await(await fetch(`/api/config/save-template`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({template:e,type:n})})).json();return r.success?(n===`description`?a.value.includes(e)||a.value.push(e):n===`scope`?o.value.includes(e)||o.value.push(e):n===`message`?l.value.includes(e)||l.value.push(e):n===`command`&&(u.value.includes(e)||u.value.push(e)),t.success(c(`@D50BB:模板已保存`)),!0):(t.error(`${c(`@D50BB:保存模板失败: `)}${r.error}`),!1)}catch(e){return t.error(`${c(`@D50BB:保存模板失败: `)}${e.message}`),!1}}async function X(e,n){try{let r=await(await fetch(`/api/config/delete-template`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({template:e,type:n})})).json();return r.success?(n===`description`?a.value=a.value.filter(t=>t!==e):n===`scope`?o.value=o.value.filter(t=>t!==e):n===`message`?l.value=l.value.filter(t=>t!==e):n===`command`&&(u.value=u.value.filter(t=>t!==e)),t.success(c(`@D50BB:模板已删除`)),!0):(t.error(`${c(`@D50BB:删除模板失败: `)}${r.error}`),!1)}catch(e){return t.error(`${c(`@D50BB:删除模板失败: `)}${e.message}`),!1}}async function Z(e,n,r){try{let i=await(await fetch(`/api/config/update-template`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({oldTemplate:e,newTemplate:n,type:r})})).json();if(i.success){if(r===`description`){let t=a.value.indexOf(e);t!==-1&&(a.value[t]=n)}else if(r===`scope`){let t=o.value.indexOf(e);t!==-1&&(o.value[t]=n)}else if(r===`message`){let t=l.value.indexOf(e);t!==-1&&(l.value[t]=n)}else if(r===`command`){let t=u.value.indexOf(e);t!==-1&&(u.value[t]=n)}return t.success(c(`@D50BB:模板已更新`)),!0}else return t.error(`${c(`@D50BB:更新模板失败: `)}${i.error}`),!1}catch(e){return t.error(`${c(`@D50BB:更新模板失败: `)}${e.message}`),!1}}async function te(e,n){try{let r=await(await fetch(`/api/config/pin-template`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({template:e,type:n})})).json();return r.success?(n===`description`?(a.value=a.value.filter(t=>t!==e),a.value.unshift(e)):n===`scope`?(o.value=o.value.filter(t=>t!==e),o.value.unshift(e)):n===`message`?(l.value=l.value.filter(t=>t!==e),l.value.unshift(e)):n===`command`&&(u.value=u.value.filter(t=>t!==e),u.value.unshift(e)),t.success(c(`@D50BB:模板已置顶`)),!0):(t.error(`${c(`@D50BB:置顶模板失败: `)}${r.error}`),!1)}catch(e){return t.error(`${c(`@D50BB:置顶模板失败: `)}${e.message}`),!1}}async function Q(){try{let e=await(await fetch(`/api/locked-files`)).json();return e.success?(d.value=e.lockedFiles||[],e.lockedFiles):(t.error(`${c(`@D50BB:加载锁定文件列表失败: `)}${e.error}`),[])}catch(e){return console.error(`加载锁定文件列表失败:`,e),t.error(`${c(`@D50BB:加载锁定文件列表失败: `)}${e.message}`),[]}}async function ne(e){try{let n=await(await fetch(`/api/lock-file`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({filePath:e})})).json();return n.success?(await Q(),t.success(`${c(`@D50BB:文件已锁定: `)}${e}`),!0):(t.error(`${c(`@D50BB:锁定文件失败: `)}${n.error}`),!1)}catch(e){return console.error(`锁定文件失败:`,e),t.error(`${c(`@D50BB:锁定文件失败: `)}${e.message}`),!1}}async function re(e){try{let n=await(await fetch(`/api/unlock-file`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({filePath:e})})).json();return n.success?(await Q(),t.success(`${c(`@D50BB:文件已解锁: `)}${e}`),!0):(t.error(`${c(`@D50BB:解锁文件失败: `)}${n.error}`),!1)}catch(e){return console.error(`解锁文件失败:`,e),t.error(`${c(`@D50BB:解锁文件失败: `)}${e.message}`),!1}}async function ie(e){try{let t=await(await fetch(`/api/check-file-lock`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({filePath:e})})).json();return t.success?t.isLocked:(console.error(`检查文件锁定状态失败:`,t.error),!1)}catch(e){return console.error(`检查文件锁定状态失败:`,e),!1}}async function ae(e){try{let n=await(await fetch(`/api/config/save-custom-command`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({command:e})})).json();return n.success?(await B(!0),t.success(c(`@CMD01:命令已保存`)),!0):(t.error(`${c(`@CMD01:保存命令失败: `)}${n.error}`),!1)}catch(e){return t.error(`${c(`@CMD01:保存命令失败: `)}${e.message}`),!1}}async function oe(e){try{let n=await(await fetch(`/api/config/delete-custom-command`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:e})})).json();return n.success?(await B(!0),t.success(c(`@CMD01:命令已删除`)),!0):(t.error(`${c(`@CMD01:删除命令失败: `)}${n.error}`),!1)}catch(e){return t.error(`${c(`@CMD01:删除命令失败: `)}${e.message}`),!1}}async function se(e,n){try{let r=await(await fetch(`/api/config/update-custom-command`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:e,command:n})})).json();return r.success?(await B(!0),t.success(c(`@CMD01:命令已更新`)),!0):(t.error(`${c(`@CMD01:更新命令失败: `)}${r.error}`),!1)}catch(e){return t.error(`${c(`@CMD01:更新命令失败: `)}${e.message}`),!1}}async function ce(e){try{let n=await(await fetch(`/api/config/pin-custom-command`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:e})})).json();return n.success?(await B(!0),!0):(t.error(n.error),!1)}catch(e){return t.error(e.message),!1}}async function le(e){try{let n=await(await fetch(`/api/config/save-orchestration`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({orchestration:e})})).json();return n.success?(await B(!0),t.success(c(`@ORCH:编排已保存`)),n.orchestration||null):(t.error(`${c(`@ORCH:保存编排失败: `)}${n.error}`),null)}catch(e){return t.error(`${c(`@ORCH:保存编排失败: `)}${e.message}`),null}}async function ue(e){try{let n=await(await fetch(`/api/config/delete-orchestration`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:e})})).json();return n.success?(await B(!0),t.success(c(`@ORCH:编排已删除`)),!0):(t.error(`${c(`@ORCH:删除编排失败: `)}${n.error}`),!1)}catch(e){return t.error(`${c(`@ORCH:删除编排失败: `)}${e.message}`),!1}}async function de(e,n){try{let r=await(await fetch(`/api/config/update-orchestration`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({id:e,orchestration:n})})).json();return r.success?(await B(!0),t.success(c(`@ORCH:编排已更新`)),!0):(t.error(`${c(`@ORCH:更新编排失败: `)}${r.error}`),!1)}catch(e){return t.error(`${c(`@ORCH:更新编排失败: `)}${e.message}`),!1}}async function fe(e,n){try{let r=await(await fetch(`/api/config/save-startup-items`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({startupItems:e,startupAutoRun:n===void 0?g.value:n})})).json();return r.success?(h.value=e,n!==void 0&&(g.value=n),!0):(t.error(`保存启动项失败: ${r.error}`),!1)}catch(e){return t.error(`保存启动项失败: ${e.message}`),!1}}async function pe(e){try{let n=await(await fetch(`/api/config/save-models`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({models:e})})).json();return n.success?(j.value=e,!0):(t.error(`${c(`@D50BB:保存模型失败: `)}${n.error}`),!1)}catch(e){return t.error(`${c(`@D50BB:保存模型失败: `)}${e.message}`),!1}}async function $(e){try{let n=await(await fetch(`/api/config/save-general-settings`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})).json();if(n.success){if(e.theme&&(k.value=e.theme,I(e.theme)),e.locale){A.value=e.locale,s(e.locale);let t=f();t.currentLocale=e.locale}return!0}else return t.error(`保存通用设置失败: ${n.error}`),!1}catch(e){return t.error(`保存通用设置失败: ${e.message}`),!1}}return{models:j,defaultCommitMessage:i,descriptionTemplates:a,scopeTemplates:o,messageTemplates:l,commandTemplates:u,lockedFiles:d,customCommands:p,orchestrations:m,startupItems:h,startupAutoRun:g,afterQuickPushAction:_,isLoading:v,isLoaded:y,configLoadError:b,configFilePath:x,hasConfigLoadError:ee,currentDirectory:S,config:R,autoClosePushModal:C,autoSetDefaultMessage:w,isStandardCommit:T,skipHooks:E,autoQuickPushOnEnter:D,pullBeforePush:O,theme:k,locale:A,ui:N,isUiLoaded:P,loadConfig:B,setCurrentDirectory:F,openSystemConfigFile:z,saveTemplate:Y,saveModels:pe,saveGeneralSettings:$,saveUiSettings:G,resetUiLayout:K,applyTheme:I,toggleTheme:L,saveDefaultMessage:q,deleteTemplate:X,updateTemplate:Z,pinTemplate:te,loadLockedFiles:Q,lockFile:ne,unlockFile:re,isFileLocked:ie,saveCustomCommand:ae,deleteCustomCommand:oe,updateCustomCommand:se,pinCustomCommand:ce,saveOrchestration:le,deleteOrchestration:ue,updateOrchestration:de,saveStartupItems:fe,saveAfterQuickPushAction:J,saveCommitSettings:V}});export{f as n,p as t};
|