zen-gitsync 2.11.2 → 2.11.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.
@@ -10,10 +10,10 @@
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-C4_PwIYq.js"></script>
14
- <link rel="modulepreload" crossorigin href="/assets/vendor-B12x6pMa.js">
15
- <link rel="stylesheet" crossorigin href="/assets/vendor-BeS3b13U.css">
16
- <link rel="stylesheet" crossorigin href="/assets/index-CeUTujBp.css">
13
+ <script type="module" crossorigin src="/assets/index-C9dA2Gmn.js"></script>
14
+ <link rel="modulepreload" crossorigin href="/assets/vendor-BSAE54oX.js">
15
+ <link rel="stylesheet" crossorigin href="/assets/vendor-COoKXBNX.css">
16
+ <link rel="stylesheet" crossorigin href="/assets/index-ByZIPrdG.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="app"></div>
@@ -33,6 +33,20 @@ export function registerConfigRoutes({
33
33
  });
34
34
  }
35
35
  });
36
+
37
+ // 删除最近访问的目录
38
+ app.post('/api/remove_recent_directory', async (req, res) => {
39
+ try {
40
+ const { path: dirPath } = req.body;
41
+ if (!dirPath) {
42
+ return res.status(400).json({ success: false, error: '目录路径不能为空' });
43
+ }
44
+ const list = await configManager.removeRecentDirectory(dirPath);
45
+ res.json({ success: true, directories: list });
46
+ } catch (error) {
47
+ res.status(500).json({ success: false, error: error.message });
48
+ }
49
+ });
36
50
 
37
51
  // 获取配置
38
52
  app.get('/api/config/getConfig', async (req, res) => {
@@ -644,6 +658,23 @@ export function registerConfigRoutes({
644
658
  }
645
659
  })
646
660
 
661
+ // 保存提交设置
662
+ app.post('/api/config/save-commit-settings', express.json(), async (req, res) => {
663
+ try {
664
+ const { isStandardCommit, skipHooks, autoQuickPushOnEnter, autoSetDefaultMessage, autoClosePushModal } = req.body
665
+ const config = await configManager.loadConfig()
666
+ if (isStandardCommit !== undefined) config.isStandardCommit = Boolean(isStandardCommit)
667
+ if (skipHooks !== undefined) config.skipHooks = Boolean(skipHooks)
668
+ if (autoQuickPushOnEnter !== undefined) config.autoQuickPushOnEnter = Boolean(autoQuickPushOnEnter)
669
+ if (autoSetDefaultMessage !== undefined) config.autoSetDefaultMessage = Boolean(autoSetDefaultMessage)
670
+ if (autoClosePushModal !== undefined) config.autoClosePushModal = Boolean(autoClosePushModal)
671
+ await configManager.saveConfig(config)
672
+ res.json({ success: true })
673
+ } catch (error) {
674
+ res.status(500).json({ success: false, error: error.message })
675
+ }
676
+ })
677
+
647
678
  // 保存"一键推送成功后启动项"
648
679
  app.post('/api/config/save-after-quick-push-action', express.json(), async (req, res) => {
649
680
  try {
@@ -3,6 +3,34 @@ import path from 'path';
3
3
  import open from 'open';
4
4
  import { spawn } from 'child_process';
5
5
 
6
+ function spawnDetached(command, args, options = {}) {
7
+ return new Promise((resolve, reject) => {
8
+ const child = spawn(command, args, {
9
+ detached: true,
10
+ stdio: 'ignore',
11
+ ...options
12
+ });
13
+
14
+ child.on('error', reject);
15
+ child.on('spawn', () => {
16
+ child.unref();
17
+ resolve('success');
18
+ });
19
+ });
20
+ }
21
+
22
+ async function launchClaudeCode(dirPath) {
23
+ if (process.platform === 'win32') {
24
+ return spawnDetached('cmd.exe', ['/c', 'start', '""', 'claude'], {
25
+ cwd: dirPath
26
+ });
27
+ }
28
+
29
+ return spawnDetached('claude', [], {
30
+ cwd: dirPath
31
+ });
32
+ }
33
+
6
34
  export function registerFileOpenRoutes({
7
35
  app
8
36
  }) {
@@ -180,11 +208,7 @@ export function registerFileOpenRoutes({
180
208
  }
181
209
 
182
210
  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
- });
211
+ await spawnDetached('code', [dirPath]);
188
212
  res.json({ success: true, message: '已用 VSCode 打开目录' });
189
213
  } catch {
190
214
  // fallback:通过 open 模块指定 code 应用
@@ -199,4 +223,32 @@ export function registerFileOpenRoutes({
199
223
  res.status(500).json({ success: false, error: error.message });
200
224
  }
201
225
  });
226
+
227
+ // 用 Claude Code 打开目录
228
+ app.post('/api/open-directory-with-claude-code', async (req, res) => {
229
+ try {
230
+ const { path: dirPath } = req.body;
231
+ if (!dirPath) {
232
+ return res.status(400).json({ success: false, error: '目录路径不能为空' });
233
+ }
234
+
235
+ try {
236
+ await fs.access(dirPath);
237
+ } catch (error) {
238
+ return res.status(400).json({ success: false, error: `目录不存在或不可访问: ${dirPath}` });
239
+ }
240
+
241
+ try {
242
+ await launchClaudeCode(dirPath);
243
+ res.json({ success: true, message: '已用 Claude Code 打开目录' });
244
+ } catch (error) {
245
+ res.status(400).json({
246
+ success: false,
247
+ error: '未检测到 Claude Code,请先安装并确保可以在终端中直接运行 claude'
248
+ });
249
+ }
250
+ } catch (error) {
251
+ res.status(500).json({ success: false, error: error.message });
252
+ }
253
+ });
202
254
  }
@@ -249,9 +249,22 @@ export function registerFsRoutes({
249
249
  try {
250
250
  // 尝试从配置中获取最近的目录
251
251
  const recentDirs = await configManager.getRecentDirectories();
252
+ // 并行检查每个目录是否存在
253
+ const checked = await Promise.all(
254
+ (recentDirs || []).map(async (dir) => {
255
+ let exists = false;
256
+ try {
257
+ await fs.access(dir);
258
+ exists = true;
259
+ } catch {
260
+ exists = false;
261
+ }
262
+ return { path: dir, exists };
263
+ })
264
+ );
252
265
  res.json({
253
266
  success: true,
254
- directories: recentDirs || []
267
+ directories: checked
255
268
  });
256
269
  } catch (error) {
257
270
  res.status(500).json({
@@ -384,6 +397,43 @@ export function registerFsRoutes({
384
397
  }
385
398
  });
386
399
 
400
+ // 新开 cmd 标签并在目标路径执行 g ui
401
+ app.post('/api/open-new-tab-gui', async (req, res) => {
402
+ try {
403
+ const directoryPath = req.body.path || process.cwd();
404
+
405
+ try {
406
+ await fs.access(directoryPath);
407
+
408
+ const platform = os.platform();
409
+
410
+ if (platform === 'win32') {
411
+ // Windows: start 第一个参数是窗口标题,必须用 "" 占位,否则 cmd 会被当成标题
412
+ // start "" /D "path" cmd /k g ui
413
+ const winPath = directoryPath.replace(/\//g, '\\').replace(/"/g, '\\"');
414
+ exec(`start "" /D "${winPath}" cmd /k g ui`);
415
+ } else if (platform === 'darwin') {
416
+ spawn('open', ['-a', 'Terminal', directoryPath], {
417
+ detached: true,
418
+ stdio: 'ignore'
419
+ }).unref();
420
+ } else {
421
+ // Linux fallback
422
+ spawn('bash', ['-c', `gnome-terminal -- bash -c "cd '${directoryPath}' && g ui; exec bash" &`], {
423
+ detached: true,
424
+ stdio: 'ignore'
425
+ }).unref();
426
+ }
427
+
428
+ res.json({ success: true });
429
+ } catch (error) {
430
+ res.status(400).json({ success: false, error: `无法打开: ${error.message}` });
431
+ }
432
+ } catch (error) {
433
+ res.status(500).json({ success: false, error: error.message });
434
+ }
435
+ });
436
+
387
437
  // ========== 文件锁定相关 API ==========
388
438
 
389
439
  // 获取锁定文件列表