zen-gitsync 2.13.28 → 2.13.30

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.
Files changed (70) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/scripts/README_COLOR_CONVERTER.md +196 -196
  4. package/scripts/README_FONTSIZE_CONVERTER.md +278 -278
  5. package/scripts/README_SPACING_CONVERTER.md +126 -126
  6. package/scripts/README_STYLE_VARS.md +180 -180
  7. package/src/ui/public/assets/AppVersionBadge-CFkZrkhd.js +2 -0
  8. package/src/ui/public/assets/{AppVersionBadge-CTUXSSX1.css → AppVersionBadge-KZ64lrZQ.css} +1 -1
  9. package/src/ui/public/assets/BranchSelector-BzaBCj1X.js +1 -0
  10. package/src/ui/public/assets/CommandConsole-DKwcJRbO.js +17 -0
  11. package/src/ui/public/assets/CommitForm-w_CCHowb.js +6 -0
  12. package/src/ui/public/assets/CustomCommandManager-C81Su49z.js +1 -0
  13. package/src/ui/public/assets/EditorView-Do6ivdLH.js +0 -0
  14. package/src/ui/public/assets/FileDiffViewer-CXMo-Rez.js +13 -0
  15. package/src/ui/public/assets/{FlowExecutionViewer-DAsEnHsE.js → FlowExecutionViewer-Bhcnqca8.js} +1 -1
  16. package/src/ui/public/assets/FlowOrchestrationWorkspace-Cb6C2RyD.js +21 -0
  17. package/src/ui/public/assets/GitStatus-D90r1nV4.js +6 -0
  18. package/src/ui/public/assets/ImagePreview-kHYBPYW5.js +1 -0
  19. package/src/ui/public/assets/LogList-D6nSWEwN.js +7 -0
  20. package/src/ui/public/assets/ProjectStartupButton-BliaT11B.js +1 -0
  21. package/src/ui/public/assets/RemoteRepoCard-CUs9RQZg.js +1 -0
  22. package/src/ui/public/assets/ResetToRemoteButton-BBsIG2FU.js +1 -0
  23. package/src/ui/public/assets/SourceMapView-Cvp-7LtP.js +3 -0
  24. package/src/ui/public/assets/TemplateManager-Clov9VqY.js +1 -0
  25. package/src/ui/public/assets/UserInputNode-yVqO69ll.js +1 -0
  26. package/src/ui/public/assets/WorkbenchView-BDYbbkM1.js +12 -0
  27. package/src/ui/public/assets/WorkbenchView-zUE7sv6z.css +1 -0
  28. package/src/ui/public/assets/{_plugin-vue_export-helper-CHzCpi8c.js → _plugin-vue_export-helper-CpTW8798.js} +3 -3
  29. package/src/ui/public/assets/element-plus-CRTdimvR.js +24 -0
  30. package/src/ui/public/assets/{flow-mindmap-mZisamFd.js → flow-mindmap-oBAz3_QK.js} +4 -4
  31. package/src/ui/public/assets/index-B666KMk3.js +7 -0
  32. package/src/ui/public/assets/vendor-BxotT5qN.js +93 -0
  33. package/src/ui/public/assets/vue-flow-Bx03URoq.js +39 -0
  34. package/src/ui/public/favicon.svg +75 -75
  35. package/src/ui/public/index.html +22 -22
  36. package/src/ui/public/logo.svg +74 -74
  37. package/src/ui/server/index.js +1 -0
  38. package/src/ui/server/routes/npm.js +161 -8
  39. package/src/ui/server/routes/workbench.js +17 -3
  40. package/src/ui/server/utils/instanceRegistry.js +11 -0
  41. package/src/ui/server/utils/startServerOnAvailablePort.js +13 -0
  42. package/src/ui/public/assets/AppVersionBadge-C63UkgfW.js +0 -2
  43. package/src/ui/public/assets/BranchSelector-Dx2OFvQn.js +0 -1
  44. package/src/ui/public/assets/CommandConsole-CWGa4uzV.js +0 -17
  45. package/src/ui/public/assets/CommitForm-DDx1W2nU.js +0 -6
  46. package/src/ui/public/assets/CustomCommandManager-COAy_rkz.js +0 -1
  47. package/src/ui/public/assets/EditorView-BI31hRI_.js +0 -0
  48. package/src/ui/public/assets/FileDiffViewer-B2BI1bGl.js +0 -13
  49. package/src/ui/public/assets/FlowOrchestrationWorkspace-DBgGQhNc.js +0 -21
  50. package/src/ui/public/assets/GitStatus-DJKc0Trp.js +0 -6
  51. package/src/ui/public/assets/ImagePreview-BrXFnZf_.js +0 -1
  52. package/src/ui/public/assets/LogList-DA4kCWzm.js +0 -7
  53. package/src/ui/public/assets/ProjectStartupButton-BkB5UNRF.js +0 -1
  54. package/src/ui/public/assets/RemoteRepoCard-DPfD9ETA.js +0 -1
  55. package/src/ui/public/assets/ResetToRemoteButton-nUKiK2JR.js +0 -1
  56. package/src/ui/public/assets/SourceMapView-B49a7U0e.js +0 -3
  57. package/src/ui/public/assets/TemplateManager-BTDGivoE.js +0 -1
  58. package/src/ui/public/assets/UserInputNode-BkdGs5AU.js +0 -1
  59. package/src/ui/public/assets/WorkbenchView-CX-g5psr.js +0 -12
  60. package/src/ui/public/assets/WorkbenchView-KpSRlP_a.css +0 -1
  61. package/src/ui/public/assets/__vite-optional-peer-dep_katex_markstream-vue-Bg9-FXxr.js +0 -1
  62. package/src/ui/public/assets/__vite-optional-peer-dep_mermaid_markstream-vue-cPZl55sA.js +0 -1
  63. package/src/ui/public/assets/__vite-optional-peer-dep_stream-markdown_markstream-vue-Dhgp-lRL.js +0 -1
  64. package/src/ui/public/assets/__vite-optional-peer-dep_stream-monaco_markstream-vue-Cr1d6De7.js +0 -1
  65. package/src/ui/public/assets/d2_markstream-vue-xMoF3M5G.js +0 -1
  66. package/src/ui/public/assets/element-plus-rcpA4EGF.js +0 -26
  67. package/src/ui/public/assets/index-BTHO-83r.js +0 -7
  68. package/src/ui/public/assets/mhchem_markstream-vue-tp86PqxG.js +0 -1
  69. package/src/ui/public/assets/vendor-DoD2dmIW.js +0 -226
  70. package/src/ui/public/assets/vue-flow-CfamgGcL.js +0 -39
@@ -1,75 +1,75 @@
1
- <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/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>
1
+ <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/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>
@@ -1,30 +1,30 @@
1
- <!doctype html>
2
- <html lang="zh-CN" translate="no">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="google" content="notranslate" />
6
- <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
- <title>Zen GitSync</title>
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-BTHO-83r.js"></script>
1
+ <!doctype html>
2
+ <html lang="zh-CN" translate="no">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="google" content="notranslate" />
6
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <title>Zen GitSync</title>
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-B666KMk3.js"></script>
14
14
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CMxvf4Kt.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/monaco-DC7mqInI.js">
16
- <link rel="modulepreload" crossorigin href="/assets/element-plus-rcpA4EGF.js">
17
- <link rel="modulepreload" crossorigin href="/assets/vendor-DoD2dmIW.js">
18
- <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CHzCpi8c.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/element-plus-CRTdimvR.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/vendor-BxotT5qN.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-CpTW8798.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/socket-io-z4n873W1.js">
20
- <link rel="modulepreload" crossorigin href="/assets/vue-flow-CfamgGcL.js">
20
+ <link rel="modulepreload" crossorigin href="/assets/vue-flow-Bx03URoq.js">
21
21
  <link rel="stylesheet" crossorigin href="/assets/monaco-UXx6YYK1.css">
22
22
  <link rel="stylesheet" crossorigin href="/assets/element-plus-CYGtZ7Nz.css">
23
23
  <link rel="stylesheet" crossorigin href="/assets/vendor-DNI_idR2.css">
24
24
  <link rel="stylesheet" crossorigin href="/assets/vue-flow-CavUTEzp.css">
25
25
  <link rel="stylesheet" crossorigin href="/assets/index-BYAxMGne.css">
26
- </head>
27
- <body>
28
- <div id="app"></div>
29
- </body>
30
- </html>
26
+ </head>
27
+ <body>
28
+ <div id="app"></div>
29
+ </body>
30
+ </html>
@@ -1,75 +1,75 @@
1
- <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/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"/>
1
+ <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/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
75
  </svg>
@@ -355,6 +355,7 @@ async function startUIServer(noOpen = false, savePort = false) {
355
355
  noOpen,
356
356
  isGitRepo,
357
357
  savePortToFile,
358
+ fsSync,
358
359
  maxTries: 100,
359
360
  callbackExecutedRef
360
361
  }).catch((e) => {
@@ -16,9 +16,11 @@ import express from 'express';
16
16
  import fs from 'fs/promises';
17
17
  import fsSync from 'fs';
18
18
  import path from 'path';
19
+ import os from 'os';
19
20
  import { exec, spawn } from 'child_process';
20
21
  import https from 'https';
21
22
  import { fileURLToPath } from 'url';
23
+ import { getRegistryPath } from '../utils/instanceRegistry.js';
22
24
 
23
25
  const __filename = fileURLToPath(import.meta.url);
24
26
  const __dirname = path.dirname(__filename);
@@ -29,9 +31,36 @@ export function registerNpmRoutes({
29
31
  }) {
30
32
  // 标记是否有升级任务在跑(防并发)
31
33
  let activeUpgrade = false;
34
+ // 标记是否有重启任务在跑(防并发)
35
+ let activeRestart = false;
36
+ // 自拉起重启相关常量
37
+ const RESTART_TIMEOUT_MS = 8000; // 子进程必须在此时间内注册到 instanceRegistry
38
+ const REGISTRY_POLL_MS = 250; // 轮询注册表文件间隔
39
+ const EXIT_DELAY_MS = 100; // 子进程就绪后给前端的最后缓冲时间
32
40
 
33
41
  // ========== 应用自升级相关 API ==========
34
42
 
43
+ // 解析"自拉起"要 spawn 的入口绝对路径。
44
+ // 优先 process.argv[1](开发态 server.js / 生产态 src/gitCommit.js 都是绝对路径),
45
+ // 但 launch.json 的 backend 是 `node -e "...process.argv = [...]; require('./server.js')"`,
46
+ // 这种篡改出来的 argv[1] 是相对路径 "server.js",不能直接 spawn。
47
+ // 回退: 用 __dirname 推导 src/ui/server/index.js 的同级 server.js(仓库根)
48
+ function resolveRestartEntry() {
49
+ const arg = process.argv[1]
50
+ if (arg && path.isAbsolute(arg) && fsSync.existsSync(arg)) {
51
+ return { entry: arg, args: process.argv.slice(2) }
52
+ }
53
+ // 回退到仓库根的 server.js(开发态入口),保留原 PORT 等环境变量
54
+ const fallback = path.resolve(__dirname, '../../../..', 'server.js')
55
+ if (fsSync.existsSync(fallback)) {
56
+ // 透传用户原始 argv[2..](--no-open 等);如果原 argv[1] 是 "server.js" 这类相对路径,
57
+ // 用户大概率没传任何 args,所以空数组也合理
58
+ const originalArgs = (arg && !path.isAbsolute(arg)) ? process.argv.slice(2) : []
59
+ return { entry: fallback, args: originalArgs }
60
+ }
61
+ return null
62
+ }
63
+
35
64
  // 当前安装的版本(从外层 package.json 读取)
36
65
  function getCurrentVersion() {
37
66
  try {
@@ -191,15 +220,139 @@ export function registerNpmRoutes({
191
220
  })
192
221
 
193
222
 
194
- // POST /api/app-restart - 优雅退出当前 Node 进程,由外层 launcher/desktop 自动拉起
223
+ // 同步读取 instanceRegistry 文件并返回 Map<pid, entry>
224
+ // npm.js 在自拉起重启场景下需要轮询此文件判断子进程是否已绑定端口
225
+ function readRegistrySync(p) {
226
+ const m = new Map();
227
+ try {
228
+ const raw = fsSync.readFileSync(p, 'utf-8');
229
+ const obj = JSON.parse(raw);
230
+ const instances = (obj && obj.instances) || {};
231
+ for (const [pidStr, entry] of Object.entries(instances)) {
232
+ const pid = Number(pidStr);
233
+ if (Number.isFinite(pid) && entry && typeof entry === 'object') {
234
+ m.set(pid, entry);
235
+ }
236
+ }
237
+ } catch (_) {
238
+ // 文件不存在或 JSON 损坏 → 返回空 Map,调用方按"还没注册"处理
239
+ }
240
+ return m;
241
+ }
242
+
243
+ // POST /api/app-restart - 自拉起重启:spawn 当前进程入口 + 轮询 instanceRegistry
244
+ // 拿到子进程端口后通过 NDJSON 流告知前端;旧进程再退出
195
245
  app.post('/api/app-restart', (_req, res) => {
196
- res.json({ success: true, message: '正在重启服务…' })
197
- // 给响应一点时间落到客户端再退出
198
- setTimeout(() => {
199
- console.log('[app-restart] 收到重启指令,退出当前进程')
200
- process.exit(0)
201
- }, 300)
202
- })
246
+ if (activeRestart) {
247
+ res.status(409).json({ success: false, error: '已有重启任务在进行中' });
248
+ return;
249
+ }
250
+ activeRestart = true;
251
+
252
+ res.setHeader('Content-Type', 'application/x-ndjson; charset=utf-8');
253
+ res.setHeader('Cache-Control', 'no-cache');
254
+ res.setHeader('Connection', 'keep-alive');
255
+ res.flushHeaders?.();
256
+
257
+ const send = (obj) => { try { res.write(JSON.stringify(obj) + '\n') } catch (_) {} };
258
+ const finish = () => { try { res.end() } catch (_) {} };
259
+ const release = () => { activeRestart = false };
260
+
261
+ // 1. 记录当前所有 instance,后续用"出现的新 pid"判定子进程
262
+ const registryPath = getRegistryPath();
263
+ const before = readRegistrySync(registryPath);
264
+ const start = Date.now();
265
+ const spawnAt = Date.now();
266
+
267
+ // 2. 准备子进程 ready 信号文件 —— startServerOnAvailablePort 在 listen 成功后会写入
268
+ // (instanceRegistry 在 EPERM 环境下不可用,这是回退通道)
269
+ const notifyPath = path.join(
270
+ os.tmpdir(),
271
+ `zen-gitsync-restart-${process.pid}-${Date.now()}.port`
272
+ );
273
+ try { fsSync.unlinkSync(notifyPath) } catch (_) {} // 清掉旧同名文件
274
+
275
+ const entryInfo = resolveRestartEntry();
276
+ if (!entryInfo) {
277
+ send({ type: 'error', message: '无法解析重启入口(process.argv[1] 既不是绝对路径,仓库根也没有 server.js)' });
278
+ finish();
279
+ release();
280
+ return;
281
+ }
282
+
283
+ let child;
284
+ try {
285
+ // spawn 当前进程当时是怎么被 node 启的同一个入口(开发态 server.js / 生产态 gitCommit.js)
286
+ // detached: true + stdio: 'ignore' + child.unref() → Windows 上父进程 exit 不会带走子进程
287
+ child = spawn(process.execPath, [entryInfo.entry, ...entryInfo.args], {
288
+ detached: true,
289
+ stdio: 'ignore',
290
+ windowsHide: true,
291
+ env: { ...process.env, ZEN_RESTART_NOTIFY_PATH: notifyPath }
292
+ });
293
+ child.unref();
294
+ } catch (err) {
295
+ send({ type: 'error', message: `拉起子进程失败: ${err?.message || err}` });
296
+ try { fsSync.unlinkSync(notifyPath) } catch (_) {}
297
+ finish();
298
+ release();
299
+ return;
300
+ }
301
+
302
+ send({ type: 'stdout', message: `[restart] 已在 PID ${child.pid} 拉起子进程 (${entryInfo.entry}),等待端口就绪…\n` });
303
+
304
+ const poll = setInterval(() => {
305
+ if (Date.now() - start > RESTART_TIMEOUT_MS) {
306
+ clearInterval(poll);
307
+ try { child.kill('SIGTERM') } catch (_) {}
308
+ send({ type: 'error', message: `子进程 ${RESTART_TIMEOUT_MS / 1000}s 内未就绪(未读到 ${notifyPath}),已 kill` });
309
+ send({ type: 'timeout' });
310
+ finish();
311
+ try { fsSync.unlinkSync(notifyPath) } catch (_) {}
312
+ // 关键:超时分支旧进程不退出,用户仍可继续手动刷新页面
313
+ release();
314
+ return;
315
+ }
316
+
317
+ // 优先判定:子进程是否写了 notify 文件(最可靠,不走 instanceRegistry)
318
+ let notifiedPort = null;
319
+ try {
320
+ const raw = fsSync.readFileSync(notifyPath, 'utf8').trim();
321
+ const p = Number(raw);
322
+ if (Number.isInteger(p) && p > 0 && p < 65536) notifiedPort = p;
323
+ } catch (_) {}
324
+
325
+ // 兜底:instanceRegistry 里出现的新 pid + port(用于没有 savePort 的环境)
326
+ let registryPort = null;
327
+ if (!notifiedPort) {
328
+ const after = readRegistrySync(registryPath);
329
+ for (const [pid, entry] of after) {
330
+ if (
331
+ !before.has(pid) &&
332
+ Number.isInteger(entry.port) && entry.port > 0 &&
333
+ (entry.lastHeartbeat ?? entry.startedAt ?? 0) >= spawnAt
334
+ ) {
335
+ registryPort = entry.port;
336
+ break;
337
+ }
338
+ }
339
+ }
340
+
341
+ const newPort = notifiedPort ?? registryPort;
342
+ if (newPort) {
343
+ clearInterval(poll);
344
+ // 给 EXIT_DELAY_MS 让新进程稳定,再通知前端 + 旧进程退出
345
+ setTimeout(() => {
346
+ send({ type: 'ready', port: newPort });
347
+ finish();
348
+ try { fsSync.unlinkSync(notifyPath) } catch (_) {}
349
+ console.log('[app-restart] 子进程已就绪,退出当前进程');
350
+ process.exit(0);
351
+ }, EXIT_DELAY_MS);
352
+ return;
353
+ }
354
+ }, REGISTRY_POLL_MS);
355
+ });
203
356
 
204
357
 
205
358
  // 读取 package.json 文件内容
@@ -2724,7 +2724,7 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
2724
2724
 
2725
2725
  // ── 续接简单任务对话 ────────────────────────────────────────────
2726
2726
  // POST /api/workbench/jobs/:id/continue
2727
- // 入参 body: { userMessage: string }
2727
+ // 入参 body: { userMessage: string, attachments?: Attachment[] }
2728
2728
  // 行为:
2729
2729
  // - 仅用于 type==='simple' 的任务,基于上一轮 job 捕获到的 claudeSessionId,
2730
2730
  // 用 claude --resume <id> 续接历史对话,**新一轮 = 新 job**
@@ -2732,6 +2732,11 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
2732
2732
  // 区分;前端按 subId 前缀聚合渲染对话流
2733
2733
  // - 上一轮还在 pending/running 时拒绝(避免 session 并发跑乱)
2734
2734
  // - 上一轮未捕获到 session_id(claude 在 init 前就 crash)时拒绝
2735
+ // - 附件处理(2026-06 改):
2736
+ // · 默认复用 task.attachments(claude --resume 上下文里已经看过这些图,
2737
+ // 不重复推,节省 token)
2738
+ // · 前端可显式传 attachments 数组做"追加"(用户续聊时新增的图片)
2739
+ // · 透传到 virtualSub.attachments,继续走 runSingleSubtask 的拼装逻辑
2735
2740
  app.post('/api/workbench/jobs/:id/continue', async (req, res) => {
2736
2741
  try {
2737
2742
  const userMessage = String(req.body?.userMessage || '').trim();
@@ -2769,6 +2774,15 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
2769
2774
  }
2770
2775
  }
2771
2776
  const nextRound = maxRound + 1;
2777
+ // 续接轮的 attachments:
2778
+ // - 前端显式传 attachments:用前端值(复用 task.attachments + 续聊时新增的)
2779
+ // - 前端没传:回退到 task.attachments(向后兼容)
2780
+ // 注意:本轮 attachments 不写盘,只用于拼 prompt,claude --resume 上下文已看过
2781
+ // 大部分图(默认复用的那些),真正"新加"的图是这次对话的关键信息。
2782
+ const bodyAtts = req.body?.attachments;
2783
+ const carryAtts = Array.isArray(bodyAtts) && bodyAtts.length > 0
2784
+ ? bodyAtts
2785
+ : (Array.isArray(task.attachments) ? task.attachments : []);
2772
2786
  const virtualSub = {
2773
2787
  id: `${task.id}__simple__r${nextRound}`,
2774
2788
  title: `续接 #${nextRound}`,
@@ -2776,8 +2790,8 @@ ${desc ? `描述:${desc}` : '描述:(无)'}${attachmentBlock}${templateB
2776
2790
  status: 'todo',
2777
2791
  // 用户输入的续接消息直接作为本轮 prompt(走 promptOverride,不再拼模板)
2778
2792
  promptOverride: userMessage,
2779
- // 续接轮不带附件,避免重复推附件给 claude(已在首轮 prompt )
2780
- attachments: []
2793
+ // 透传:复用 task 上已有附件 + 本轮新增(如果有)
2794
+ attachments: carryAtts
2781
2795
  };
2782
2796
  const repoPath = typeof getCurrentProjectPath === 'function' ? getCurrentProjectPath() : '';
2783
2797
  res.json({ success: true, message: '已加入续接队列' });
@@ -17,10 +17,21 @@
17
17
  // 多进程并发写采用 atomic temp+rename + 进程内串行化 Promise 链
18
18
  // stale 判定:PID 不存在 或 lastHeartbeat 超过阈值
19
19
 
20
+ import nodePath from 'node:path';
21
+ import nodeOs from 'node:os';
22
+
20
23
  const STALE_MS = 30_000; // 心跳超时阈值(毫秒)
21
24
  const WATCH_DEBOUNCE_MS = 100; // fs.watch 防抖时间
22
25
  const REGISTRY_VERSION = 1;
23
26
 
27
+ /**
28
+ * 默认注册表文件路径(与 createInstanceRegistry({ registryPath }) 的约定一致)
29
+ * 用于:npm.js 的 /api/app-restart 自拉起时,父进程轮询此路径判断子进程是否就绪。
30
+ */
31
+ export function getRegistryPath() {
32
+ return nodePath.join(nodeOs.homedir(), '.zen-gitsync-instances.json');
33
+ }
34
+
24
35
  function isProcessAlive(pid) {
25
36
  if (!Number.isInteger(pid) || pid <= 0) return false;
26
37
  try {