zen-gitsync 2.10.24 → 2.11.1

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.
@@ -1,27 +1,75 @@
1
1
  <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
2
- <!-- 主分支 -->
3
- <line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
4
-
5
- <!-- 特性分支 -->
6
- <path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
7
-
8
- <!-- 开发分支 -->
9
- <path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
10
-
11
- <!-- 修复分支 -->
12
- <path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
13
-
14
- <!-- 合并点 -->
15
- <path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
16
-
17
- <!-- 节点 -->
18
- <circle cx="40" cy="40" r="10" fill="#34495e" />
19
- <circle cx="40" cy="70" r="10" fill="#34495e" />
20
- <circle cx="40" cy="100" r="10" fill="#34495e" />
21
- <circle cx="40" cy="130" r="10" fill="#34495e" />
22
- <circle cx="40" cy="145" r="10" fill="#34495e" />
23
- <circle cx="40" cy="160" r="10" fill="#34495e" />
24
- <circle cx="80" cy="130" r="10" fill="#3498db" />
25
- <circle cx="100" cy="160" r="10" fill="#2ecc71" />
26
- <circle cx="120" cy="160" r="10" fill="#e74c3c" />
27
- </svg>
2
+ <defs>
3
+ <radialGradient id="bgGrad" cx="38%" cy="32%" r="70%">
4
+ <stop offset="0%" stop-color="#1e1b4b"/>
5
+ <stop offset="100%" stop-color="#0d0b1e"/>
6
+ </radialGradient>
7
+ <linearGradient id="mainGrad" x1="0%" y1="0%" x2="0%" y2="100%">
8
+ <stop offset="0%" stop-color="#c4b5fd"/>
9
+ <stop offset="100%" stop-color="#7c3aed"/>
10
+ </linearGradient>
11
+ <linearGradient id="featGrad" x1="0%" y1="0%" x2="100%" y2="100%">
12
+ <stop offset="0%" stop-color="#7dd3fc"/>
13
+ <stop offset="100%" stop-color="#0284c7"/>
14
+ </linearGradient>
15
+ <filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
16
+ <feGaussianBlur in="SourceGraphic" stdDeviation="2.5" result="blur"/>
17
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
18
+ </filter>
19
+ <filter id="softGlow" x="-20%" y="-20%" width="140%" height="140%">
20
+ <feGaussianBlur in="SourceGraphic" stdDeviation="1.5" result="blur"/>
21
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
22
+ </filter>
23
+ </defs>
24
+
25
+ <!-- 背景圆 -->
26
+ <circle cx="100" cy="100" r="96" fill="url(#bgGrad)"/>
27
+
28
+ <!-- 同步外弧(上半,代表 sync 循环) -->
29
+ <path d="M 100,20 A 80,80 0 1,1 36,153"
30
+ stroke="rgba(167,139,250,0.18)" stroke-width="4.5" fill="none" stroke-linecap="round"/>
31
+ <!-- 同步外弧(下半) -->
32
+ <path d="M 36,153 A 80,80 0 0,1 164,153"
33
+ stroke="rgba(56,189,248,0.18)" stroke-width="4.5" fill="none" stroke-linecap="round"/>
34
+ <!-- 同步箭头(上) -->
35
+ <path d="M 91,17 L 100,20 L 96,29"
36
+ stroke="rgba(167,139,250,0.55)" stroke-width="2.5" fill="none"
37
+ stroke-linecap="round" stroke-linejoin="round"/>
38
+ <!-- 同步箭头(下) -->
39
+ <path d="M 163,145 L 164,154 L 155,151"
40
+ stroke="rgba(56,189,248,0.55)" stroke-width="2.5" fill="none"
41
+ stroke-linecap="round" stroke-linejoin="round"/>
42
+
43
+ <!-- 主分支竖线 -->
44
+ <line x1="76" y1="44" x2="76" y2="156"
45
+ stroke="url(#mainGrad)" stroke-width="5.5" stroke-linecap="round"
46
+ filter="url(#softGlow)"/>
47
+
48
+ <!-- 特性分支曲线(分出) -->
49
+ <path d="M 76,80 C 98,80 130,96 130,120 L 130,140"
50
+ stroke="url(#featGrad)" stroke-width="4.5" fill="none"
51
+ stroke-linecap="round" filter="url(#softGlow)"/>
52
+ <!-- 特性分支合并回主分支 -->
53
+ <path d="M 130,140 C 130,156 105,159 76,155"
54
+ stroke="url(#featGrad)" stroke-width="4.5" fill="none" stroke-linecap="round"/>
55
+
56
+ <!-- 主分支节点 -->
57
+ <circle cx="76" cy="48" r="11" fill="#13103a" stroke="#c4b5fd" stroke-width="3" filter="url(#glow)"/>
58
+ <circle cx="76" cy="48" r="5" fill="#ddd6fe"/>
59
+
60
+ <circle cx="76" cy="84" r="11" fill="#13103a" stroke="#a78bfa" stroke-width="3"/>
61
+ <circle cx="76" cy="84" r="5" fill="#c4b5fd"/>
62
+
63
+ <circle cx="76" cy="120" r="11" fill="#13103a" stroke="#8b5cf6" stroke-width="3"/>
64
+ <circle cx="76" cy="120" r="5" fill="#a78bfa"/>
65
+
66
+ <circle cx="76" cy="155" r="11" fill="#13103a" stroke="#7c3aed" stroke-width="3" filter="url(#glow)"/>
67
+ <circle cx="76" cy="155" r="5" fill="#8b5cf6"/>
68
+
69
+ <!-- 特性分支节点 -->
70
+ <circle cx="130" cy="115" r="11" fill="#13103a" stroke="#38bdf8" stroke-width="3" filter="url(#glow)"/>
71
+ <circle cx="130" cy="115" r="5" fill="#7dd3fc"/>
72
+
73
+ <circle cx="130" cy="143" r="11" fill="#13103a" stroke="#0ea5e9" stroke-width="3" filter="url(#glow)"/>
74
+ <circle cx="130" cy="143" r="5" fill="#38bdf8"/>
75
+ </svg>
@@ -6,10 +6,14 @@
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
8
  <title>Zen GitSync</title>
9
- <script type="module" crossorigin src="/assets/index-b9vh2ATa.js"></script>
10
- <link rel="modulepreload" crossorigin href="/assets/vendor-DrABRxF7.js">
11
- <link rel="stylesheet" crossorigin href="/assets/vendor-DpSHka1A.css">
12
- <link rel="stylesheet" crossorigin href="/assets/index-D_n92IoC.css">
9
+ <!-- Premium fonts: Plus Jakarta Sans (UI) + JetBrains Mono (code) -->
10
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
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-BNeQXOl6.js"></script>
14
+ <link rel="modulepreload" crossorigin href="/assets/vendor-DwW0e1GC.js">
15
+ <link rel="stylesheet" crossorigin href="/assets/vendor-BnSvNs4p.css">
16
+ <link rel="stylesheet" crossorigin href="/assets/index-Da2l5_i3.css">
13
17
  </head>
14
18
  <body>
15
19
  <div id="app"></div>
@@ -1,27 +1,75 @@
1
1
  <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
2
- <!-- 主分支 -->
3
- <line x1="40" y1="40" x2="40" y2="160" stroke="#34495e" stroke-width="8" stroke-linecap="round" />
4
-
5
- <!-- 特性分支 -->
6
- <path d="M40,70 C60,70 80,90 80,110 L80,130" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
7
-
8
- <!-- 开发分支 -->
9
- <path d="M40,100 C60,100 100,110 100,130 L100,160" stroke="#2ecc71" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
10
-
11
- <!-- 修复分支 -->
12
- <path d="M40,130 C60,130 120,140 120,160" stroke="#e74c3c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
13
-
14
- <!-- 合并点 -->
15
- <path d="M80,130 C80,145 60,145 40,145" stroke="#3498db" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" fill="none" />
16
-
17
- <!-- 节点 -->
18
- <circle cx="40" cy="40" r="10" fill="#34495e" />
19
- <circle cx="40" cy="70" r="10" fill="#34495e" />
20
- <circle cx="40" cy="100" r="10" fill="#34495e" />
21
- <circle cx="40" cy="130" r="10" fill="#34495e" />
22
- <circle cx="40" cy="145" r="10" fill="#34495e" />
23
- <circle cx="40" cy="160" r="10" fill="#34495e" />
24
- <circle cx="80" cy="130" r="10" fill="#3498db" />
25
- <circle cx="100" cy="160" r="10" fill="#2ecc71" />
26
- <circle cx="120" cy="160" r="10" fill="#e74c3c" />
2
+ <defs>
3
+ <radialGradient id="bgGrad" cx="38%" cy="32%" r="70%">
4
+ <stop offset="0%" stop-color="#1e1b4b"/>
5
+ <stop offset="100%" stop-color="#0d0b1e"/>
6
+ </radialGradient>
7
+ <linearGradient id="mainGrad" x1="0%" y1="0%" x2="0%" y2="100%">
8
+ <stop offset="0%" stop-color="#c4b5fd"/>
9
+ <stop offset="100%" stop-color="#7c3aed"/>
10
+ </linearGradient>
11
+ <linearGradient id="featGrad" x1="0%" y1="0%" x2="100%" y2="100%">
12
+ <stop offset="0%" stop-color="#7dd3fc"/>
13
+ <stop offset="100%" stop-color="#0284c7"/>
14
+ </linearGradient>
15
+ <filter id="glow" x="-40%" y="-40%" width="180%" height="180%">
16
+ <feGaussianBlur in="SourceGraphic" stdDeviation="2.5" result="blur"/>
17
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
18
+ </filter>
19
+ <filter id="softGlow" x="-20%" y="-20%" width="140%" height="140%">
20
+ <feGaussianBlur in="SourceGraphic" stdDeviation="1.5" result="blur"/>
21
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
22
+ </filter>
23
+ </defs>
24
+
25
+ <!-- 背景�?-->
26
+ <circle cx="100" cy="100" r="96" fill="url(#bgGrad)"/>
27
+
28
+ <!-- 同步外弧(上半,代表 sync 循环�?-->
29
+ <path d="M 100,20 A 80,80 0 1,1 36,153"
30
+ stroke="rgba(167,139,250,0.18)" stroke-width="4.5" fill="none" stroke-linecap="round"/>
31
+ <!-- 同步外弧(下半) -->
32
+ <path d="M 36,153 A 80,80 0 0,1 164,153"
33
+ stroke="rgba(56,189,248,0.18)" stroke-width="4.5" fill="none" stroke-linecap="round"/>
34
+ <!-- 同步箭头(上�?-->
35
+ <path d="M 91,17 L 100,20 L 96,29"
36
+ stroke="rgba(167,139,250,0.55)" stroke-width="2.5" fill="none"
37
+ stroke-linecap="round" stroke-linejoin="round"/>
38
+ <!-- 同步箭头(下�?-->
39
+ <path d="M 163,145 L 164,154 L 155,151"
40
+ stroke="rgba(56,189,248,0.55)" stroke-width="2.5" fill="none"
41
+ stroke-linecap="round" stroke-linejoin="round"/>
42
+
43
+ <!-- 主分支竖�?-->
44
+ <line x1="76" y1="44" x2="76" y2="156"
45
+ stroke="url(#mainGrad)" stroke-width="5.5" stroke-linecap="round"
46
+ filter="url(#softGlow)"/>
47
+
48
+ <!-- 特性分支曲线(分出�?-->
49
+ <path d="M 76,80 C 98,80 130,96 130,120 L 130,140"
50
+ stroke="url(#featGrad)" stroke-width="4.5" fill="none"
51
+ stroke-linecap="round" filter="url(#softGlow)"/>
52
+ <!-- 特性分支合并回主分�?-->
53
+ <path d="M 130,140 C 130,156 105,159 76,155"
54
+ stroke="url(#featGrad)" stroke-width="4.5" fill="none" stroke-linecap="round"/>
55
+
56
+ <!-- 主分支节�?-->
57
+ <circle cx="76" cy="48" r="11" fill="#13103a" stroke="#c4b5fd" stroke-width="3" filter="url(#glow)"/>
58
+ <circle cx="76" cy="48" r="5" fill="#ddd6fe"/>
59
+
60
+ <circle cx="76" cy="84" r="11" fill="#13103a" stroke="#a78bfa" stroke-width="3"/>
61
+ <circle cx="76" cy="84" r="5" fill="#c4b5fd"/>
62
+
63
+ <circle cx="76" cy="120" r="11" fill="#13103a" stroke="#8b5cf6" stroke-width="3"/>
64
+ <circle cx="76" cy="120" r="5" fill="#a78bfa"/>
65
+
66
+ <circle cx="76" cy="155" r="11" fill="#13103a" stroke="#7c3aed" stroke-width="3" filter="url(#glow)"/>
67
+ <circle cx="76" cy="155" r="5" fill="#8b5cf6"/>
68
+
69
+ <!-- 特性分支节�?-->
70
+ <circle cx="130" cy="115" r="11" fill="#13103a" stroke="#38bdf8" stroke-width="3" filter="url(#glow)"/>
71
+ <circle cx="130" cy="115" r="5" fill="#7dd3fc"/>
72
+
73
+ <circle cx="130" cy="143" r="11" fill="#13103a" stroke="#0ea5e9" stroke-width="3" filter="url(#glow)"/>
74
+ <circle cx="130" cy="143" r="5" fill="#38bdf8"/>
27
75
  </svg>
@@ -2,7 +2,7 @@ import express from 'express';
2
2
  import { createServer } from 'http';
3
3
  import { fileURLToPath } from 'url';
4
4
  import path from 'path';
5
- import { execGitCommand, getCommandHistory, addCommandToHistory, clearCommandHistory, registerSocketIO, execGitAddWithLockFilter } from '../../utils/index.js';
5
+ import { execGitCommand, getCommandHistory, addCommandToHistory, clearCommandHistory, registerSocketIO, execGitAddWithLockFilter, checkAndClearGitLock } from '../../utils/index.js';
6
6
  import open from 'open';
7
7
  import config from '../../config.js';
8
8
  import chalk from 'chalk';
@@ -230,6 +230,7 @@ async function startUIServer(noOpen = false, savePort = false) {
230
230
  execGitAddWithLockFilter,
231
231
  addCommandToHistory,
232
232
  clearCommandHistory,
233
+ checkAndClearGitLock,
233
234
  getIsGitRepo: () => isGitRepo,
234
235
  setRecentPushStatus: (v) => { recentPushStatus = v; }
235
236
  });
@@ -170,4 +170,33 @@ export function registerFileOpenRoutes({
170
170
  });
171
171
  }
172
172
  });
173
+
174
+ // 用 VSCode 打开目录
175
+ app.post('/api/open-directory-with-vscode', async (req, res) => {
176
+ try {
177
+ const { path: dirPath } = req.body;
178
+ if (!dirPath) {
179
+ return res.status(400).json({ success: false, error: '目录路径不能为空' });
180
+ }
181
+
182
+ try {
183
+ await new Promise((resolve, reject) => {
184
+ const vscodeProcess = spawn('code', [dirPath], { detached: true, stdio: 'ignore' });
185
+ vscodeProcess.on('error', reject);
186
+ vscodeProcess.on('spawn', () => { vscodeProcess.unref(); resolve('success'); });
187
+ });
188
+ res.json({ success: true, message: '已用 VSCode 打开目录' });
189
+ } catch {
190
+ // fallback:通过 open 模块指定 code 应用
191
+ try {
192
+ await open(dirPath, { app: { name: 'code' } });
193
+ res.json({ success: true, message: '已用 VSCode 打开目录' });
194
+ } catch (openError) {
195
+ res.status(400).json({ success: false, error: 'VSCode 可能未安装或未添加到 PATH' });
196
+ }
197
+ }
198
+ } catch (error) {
199
+ res.status(500).json({ success: false, error: error.message });
200
+ }
201
+ });
173
202
  }
@@ -16,6 +16,7 @@ export function registerGitOpsRoutes({
16
16
  execGitAddWithLockFilter,
17
17
  addCommandToHistory,
18
18
  clearCommandHistory,
19
+ checkAndClearGitLock,
19
20
  getIsGitRepo,
20
21
  setRecentPushStatus
21
22
  }) {
@@ -643,6 +644,9 @@ export function registerGitOpsRoutes({
643
644
  });
644
645
  }
645
646
 
647
+ // 尝试清理 Git 锁文件
648
+ await checkAndClearGitLock();
649
+
646
650
  // 执行 git reset --hard origin/branch 命令
647
651
  await execGitCommand(`git reset --hard origin/${branch}`);
648
652
  res.json({ success: true });
@@ -655,6 +659,28 @@ export function registerGitOpsRoutes({
655
659
  }
656
660
  });
657
661
 
662
+ // 清除本地所有更改,包括未跟踪文件 (git reset --hard && git clean -fd)
663
+ app.post('/api/discard-all-changes', async (req, res) => {
664
+ try {
665
+ // 尝试清理 Git 锁文件
666
+ await checkAndClearGitLock();
667
+
668
+ // 1. 执行 git reset --hard 丢弃已跟踪文件的更改
669
+ await execGitCommand('git reset --hard');
670
+
671
+ // 2. 执行 git clean -fd 移除未跟踪的文件和目录
672
+ await execGitCommand('git clean -fd');
673
+
674
+ res.json({ success: true });
675
+ } catch (error) {
676
+ console.error('清除所有更改失败:', error);
677
+ res.status(500).json({
678
+ success: false,
679
+ error: `清除所有更改失败: ${error.message}`
680
+ });
681
+ }
682
+ });
683
+
658
684
  // 获取提交的文件列表
659
685
  app.get('/api/commit-files', async (req, res) => {
660
686
  try {
@@ -365,6 +365,39 @@ function execGitCommand(command, options = {}) {
365
365
  })
366
366
  }
367
367
 
368
+ /**
369
+ * 检查并尝试清理 Git 锁文件
370
+ * @returns {Promise<boolean>} 是否清理成功
371
+ */
372
+ async function checkAndClearGitLock() {
373
+ try {
374
+ const cwd = getCwd();
375
+ let gitRoot;
376
+ try {
377
+ // 使用 execSync 快速获取 Git 根目录
378
+ const rootOutput = execSync('git rev-parse --show-toplevel', { cwd, encoding: 'utf-8' });
379
+ gitRoot = rootOutput.trim();
380
+ } catch (e) {
381
+ gitRoot = cwd;
382
+ }
383
+
384
+ const lockFilePath = path.join(gitRoot, '.git', 'index.lock');
385
+ try {
386
+ await fs.access(lockFilePath);
387
+ // 如果文件存在,尝试删除它
388
+ await fs.unlink(lockFilePath);
389
+ console.log(chalk.green(`✅ 已清理 Git 锁文件: ${lockFilePath}`));
390
+ return true;
391
+ } catch (e) {
392
+ // 文件不存在,不需要清理
393
+ return false;
394
+ }
395
+ } catch (error) {
396
+ console.error(chalk.red('清理 Git 锁文件失败:'), error.message);
397
+ return false;
398
+ }
399
+ }
400
+
368
401
  // Function to get command history
369
402
  function getCommandHistory() {
370
403
  return [...commandHistory];
@@ -1001,6 +1034,7 @@ export {
1001
1034
  coloredLog, errorLog, execSyncGitCommand,
1002
1035
  execGitCommand, getCommandHistory, addCommandToHistory, // Add command history exports
1003
1036
  clearCommandHistory,
1037
+ checkAndClearGitLock,
1004
1038
  registerSocketIO, // 导出注册Socket.io的函数
1005
1039
  getCwd, judgePlatform, showHelp, judgeLog, printGitLog,
1006
1040
  judgeHelp, exec_exit, judgeUnmerged, delay, formatDuration,