zen-gitsync 2.1.3 → 2.1.6

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 (38) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +96 -96
  3. package/index.js +2 -2
  4. package/package.json +69 -66
  5. package/src/config.js +51 -51
  6. package/src/gitCommit.js +261 -261
  7. package/src/ui/public/assets/index-8gQo1ABk.js +20 -0
  8. package/src/ui/public/assets/index-IcGOG2Ja.css +1 -0
  9. package/src/ui/public/assets/{vendor-Dy1zosHw.js → vendor-Bm8yNvvz.js} +1 -1
  10. package/src/ui/public/favicon.svg +26 -26
  11. package/src/ui/public/index.html +3 -3
  12. package/src/ui/public/logo.svg +26 -26
  13. package/src/ui/server/index.js +141 -51
  14. package/src/ui/client/README.md +0 -5
  15. package/src/ui/client/auto-imports.d.ts +0 -10
  16. package/src/ui/client/components.d.ts +0 -33
  17. package/src/ui/client/index.html +0 -13
  18. package/src/ui/client/package.json +0 -28
  19. package/src/ui/client/public/favicon.svg +0 -27
  20. package/src/ui/client/public/logo.svg +0 -27
  21. package/src/ui/client/public/vite.svg +0 -1
  22. package/src/ui/client/src/App.vue +0 -984
  23. package/src/ui/client/src/assets/logo.svg +0 -27
  24. package/src/ui/client/src/components/CommitForm.vue +0 -2158
  25. package/src/ui/client/src/components/GitStatus.vue +0 -1621
  26. package/src/ui/client/src/components/LogList.vue +0 -1937
  27. package/src/ui/client/src/main.ts +0 -7
  28. package/src/ui/client/src/stores/configStore.ts +0 -212
  29. package/src/ui/client/src/stores/gitLogStore.ts +0 -790
  30. package/src/ui/client/src/stores/gitStore.ts +0 -443
  31. package/src/ui/client/src/vite-env.d.ts +0 -1
  32. package/src/ui/client/stats.html +0 -4949
  33. package/src/ui/client/tsconfig.app.json +0 -14
  34. package/src/ui/client/tsconfig.json +0 -7
  35. package/src/ui/client/tsconfig.node.json +0 -24
  36. package/src/ui/client/vite.config.ts +0 -50
  37. package/src/ui/public/assets/index-CekdGwXY.js +0 -20
  38. package/src/ui/public/assets/index-SwSkrgGk.css +0 -1
@@ -1,27 +1,27 @@
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" />
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
27
  </svg>
@@ -5,10 +5,10 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Zen-GitSync - Git同步工具</title>
8
- <script type="module" crossorigin src="/assets/index-CekdGwXY.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/vendor-Dy1zosHw.js">
8
+ <script type="module" crossorigin src="/assets/index-8gQo1ABk.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/vendor-Bm8yNvvz.js">
10
10
  <link rel="stylesheet" crossorigin href="/assets/vendor-Dp0FkvMe.css">
11
- <link rel="stylesheet" crossorigin href="/assets/index-SwSkrgGk.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-IcGOG2Ja.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="app"></div>
@@ -1,27 +1,27 @@
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" />
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
27
  </svg>
@@ -52,7 +52,7 @@ async function startUIServer() {
52
52
  });
53
53
  app.get('/api/status_porcelain', async (req, res) => {
54
54
  try {
55
- const { stdout } = await execGitCommand('git status --porcelain');
55
+ const { stdout } = await execGitCommand('git status --porcelain --untracked-files=all');
56
56
  res.json({ status: stdout });
57
57
  } catch (error) {
58
58
  res.status(500).json({ error: error.message });
@@ -72,67 +72,37 @@ async function startUIServer() {
72
72
  // 获取分支与远程的差异状态(领先/落后提交数)
73
73
  app.get('/api/branch-status', async (req, res) => {
74
74
  try {
75
- // 先获取当前分支名
76
- const { stdout: branchName } = await execGitCommand('git rev-parse --abbrev-ref HEAD');
77
- const currentBranch = branchName.trim();
75
+ // 检查当前目录是否是Git仓库
76
+ if (!isGitRepo) {
77
+ return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
78
+ }
78
79
 
79
- // 获取远程仓库名称
80
- const { stdout: remoteName } = await execGitCommand('git remote');
81
- const remote = remoteName.trim() || 'origin';
80
+ // 获取当前分支
81
+ const { stdout: branchOutput } = await execGitCommand('git symbolic-ref --short HEAD');
82
+ const currentBranch = branchOutput.trim();
82
83
 
83
- // 尝试获取当前分支的上游分支
84
- let upstreamBranch = '';
85
- let hasUpstream = false;
84
+ // 获取上游分支
85
+ const { stdout: upstreamOutput } = await execGitCommand('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { ignoreError: true });
86
86
 
87
- try {
88
- const { stdout: upstream } = await execGitCommand(`git rev-parse --abbrev-ref ${currentBranch}@{upstream}`);
89
- upstreamBranch = upstream.trim();
90
- hasUpstream = true;
91
- } catch (error) {
92
- // 没有上游分支设置,这是正常的
93
- console.log(`分支 ${currentBranch} 没有设置上游分支`);
94
- hasUpstream = false;
87
+ if (!upstreamOutput.trim()) {
88
+ // 没有上游分支
89
+ return res.json({ hasUpstream: false, ahead: 0, behind: 0 });
95
90
  }
96
91
 
97
- // 如果分支没有上游设置,直接返回结果
98
- if (!hasUpstream) {
99
- return res.json({
100
- branch: currentBranch,
101
- remote,
102
- hasUpstream: false,
103
- ahead: 0,
104
- behind: 0
105
- });
106
- }
92
+ const upstreamBranch = upstreamOutput.trim();
107
93
 
108
- // 获取领先和落后的提交数
109
- const { stdout: statusCompare } = await execGitCommand(`git rev-list --count --left-right ${currentBranch}...${upstreamBranch}`);
110
-
111
- // 解析结果,格式通常是 "1 2" 表示领先1个提交,落后2个提交
112
- let ahead = 0;
113
- let behind = 0;
114
-
115
- if (statusCompare && statusCompare.trim()) {
116
- const parts = statusCompare.trim().split(/\s+/);
117
- if (parts.length >= 2) {
118
- ahead = parseInt(parts[0]) || 0;
119
- behind = parseInt(parts[1]) || 0;
120
- } else if (parts.length === 1) {
121
- // 只有一个数字的情况,通常是领先的提交数
122
- ahead = parseInt(parts[0]) || 0;
123
- }
124
- }
94
+ // 获取领先/落后提交数
95
+ const { stdout: aheadBehindOutput } = await execGitCommand(`git rev-list --left-right --count ${currentBranch}...${upstreamBranch}`);
96
+ const [ahead, behind] = aheadBehindOutput.trim().split('\t').map(Number);
125
97
 
126
98
  res.json({
127
- branch: currentBranch,
128
- remote,
129
- upstreamBranch,
130
99
  hasUpstream: true,
100
+ upstreamBranch,
131
101
  ahead,
132
102
  behind
133
103
  });
134
104
  } catch (error) {
135
- console.error("获取分支状态失败:", error);
105
+ console.error('获取分支状态失败:', error);
136
106
  res.status(500).json({ error: error.message });
137
107
  }
138
108
  });
@@ -359,6 +329,101 @@ async function startUIServer() {
359
329
  }
360
330
  });
361
331
 
332
+ // POST接口版本的浏览目录功能
333
+ app.post('/api/browse_directory', async (req, res) => {
334
+ try {
335
+ // 获取要浏览的目录路径,如果没有提供,则使用当前目录
336
+ const directoryPath = req.body.currentPath || process.cwd();
337
+
338
+ try {
339
+ // 读取目录内容
340
+ const items = await fs.readdir(directoryPath, { withFileTypes: true });
341
+
342
+ // 分离文件夹和文件
343
+ const directories = [];
344
+ const files = [];
345
+
346
+ for (const item of items) {
347
+ const fullPath = path.join(directoryPath, item.name);
348
+ if (item.isDirectory()) {
349
+ directories.push({
350
+ name: item.name,
351
+ path: fullPath,
352
+ type: 'directory'
353
+ });
354
+ }
355
+ }
356
+
357
+ // 只返回目录,不返回文件
358
+ directories.sort((a, b) => a.name.localeCompare(b.name));
359
+
360
+ // 获取父目录路径
361
+ const parentPath = path.dirname(directoryPath);
362
+
363
+ // 返回选择的目录路径
364
+ res.json({
365
+ success: true,
366
+ path: directoryPath,
367
+ parentPath: parentPath !== directoryPath ? parentPath : null,
368
+ items: directories
369
+ });
370
+ } catch (error) {
371
+ res.status(400).json({
372
+ success: false,
373
+ error: `无法读取目录 "${directoryPath}": ${error.message}`
374
+ });
375
+ }
376
+ } catch (error) {
377
+ res.status(500).json({
378
+ success: false,
379
+ error: error.message
380
+ });
381
+ }
382
+ });
383
+
384
+ // 获取最近访问的目录列表
385
+ app.get('/api/recent_directories', async (req, res) => {
386
+ try {
387
+ // 尝试从配置中获取最近的目录
388
+ const recentDirs = await configManager.getRecentDirectories();
389
+ res.json({
390
+ success: true,
391
+ directories: recentDirs || []
392
+ });
393
+ } catch (error) {
394
+ res.status(500).json({
395
+ success: false,
396
+ error: error.message
397
+ });
398
+ }
399
+ });
400
+
401
+ // 保存最近访问的目录
402
+ app.post('/api/save_recent_directory', async (req, res) => {
403
+ try {
404
+ const { path } = req.body;
405
+
406
+ if (!path) {
407
+ return res.status(400).json({
408
+ success: false,
409
+ error: '目录路径不能为空'
410
+ });
411
+ }
412
+
413
+ // 保存到配置
414
+ await configManager.saveRecentDirectory(path);
415
+
416
+ res.json({
417
+ success: true
418
+ });
419
+ } catch (error) {
420
+ res.status(500).json({
421
+ success: false,
422
+ error: error.message
423
+ });
424
+ }
425
+ });
426
+
362
427
  // 获取配置
363
428
  app.get('/api/config/getConfig', async (req, res) => {
364
429
  try {
@@ -1263,7 +1328,32 @@ async function startUIServer() {
1263
1328
  }
1264
1329
  });
1265
1330
 
1266
- // 获取所有作者列表
1331
+ // 获取远程仓库URL的API
1332
+ app.get('/api/remote-url', async (req, res) => {
1333
+ try {
1334
+ // 检查当前目录是否是Git仓库
1335
+ if (!isGitRepo) {
1336
+ return res.json({ success: false, error: '当前目录不是Git仓库' });
1337
+ }
1338
+
1339
+ // 执行git命令获取远程仓库URL
1340
+ const { stdout } = await execGitCommand('git config --get remote.origin.url');
1341
+
1342
+ // 返回远程仓库URL
1343
+ res.json({
1344
+ success: true,
1345
+ url: stdout.trim()
1346
+ });
1347
+ } catch (error) {
1348
+ console.error('获取远程仓库URL失败:', error);
1349
+ res.json({
1350
+ success: false,
1351
+ error: error.message || '获取远程仓库URL失败'
1352
+ });
1353
+ }
1354
+ });
1355
+
1356
+ // 获取所有作者列表
1267
1357
  app.get('/api/authors', async (req, res) => {
1268
1358
  try {
1269
1359
  // 使用git命令获取所有提交者,不依赖Unix命令
@@ -1394,7 +1484,7 @@ async function startUIServer() {
1394
1484
  const { stdout: statusOutput } = await execGitCommand('git status');
1395
1485
 
1396
1486
  // 获取porcelain格式状态
1397
- const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain');
1487
+ const { stdout: porcelainOutput } = await execGitCommand('git status --porcelain --untracked-files=all');
1398
1488
 
1399
1489
  // 广播到所有连接的客户端
1400
1490
  io.emit('git_status_update', {
@@ -1,5 +0,0 @@
1
- # Vue 3 + TypeScript + Vite
2
-
3
- This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
4
-
5
- Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
@@ -1,10 +0,0 @@
1
- /* eslint-disable */
2
- /* prettier-ignore */
3
- // @ts-nocheck
4
- // noinspection JSUnusedGlobalSymbols
5
- // Generated by unplugin-auto-import
6
- // biome-ignore lint: disable
7
- export {}
8
- declare global {
9
-
10
- }
@@ -1,33 +0,0 @@
1
- /* eslint-disable */
2
- // @ts-nocheck
3
- // Generated by unplugin-vue-components
4
- // Read more: https://github.com/vuejs/core/pull/3399
5
- // biome-ignore lint: disable
6
- export {}
7
-
8
- /* prettier-ignore */
9
- declare module 'vue' {
10
- export interface GlobalComponents {
11
- CommitForm: typeof import('./src/components/CommitForm.vue')['default']
12
- ElAlert: typeof import('element-plus/es')['ElAlert']
13
- ElButton: typeof import('element-plus/es')['ElButton']
14
- ElCard: typeof import('element-plus/es')['ElCard']
15
- ElDialog: typeof import('element-plus/es')['ElDialog']
16
- ElEmpty: typeof import('element-plus/es')['ElEmpty']
17
- ElForm: typeof import('element-plus/es')['ElForm']
18
- ElFormItem: typeof import('element-plus/es')['ElFormItem']
19
- ElIcon: typeof import('element-plus/es')['ElIcon']
20
- ElInput: typeof import('element-plus/es')['ElInput']
21
- ElOption: typeof import('element-plus/es')['ElOption']
22
- ElRow: typeof import('element-plus/es')['ElRow']
23
- ElSelect: typeof import('element-plus/es')['ElSelect']
24
- ElSwitch: typeof import('element-plus/es')['ElSwitch']
25
- ElTag: typeof import('element-plus/es')['ElTag']
26
- ElTooltip: typeof import('element-plus/es')['ElTooltip']
27
- GitStatus: typeof import('./src/components/GitStatus.vue')['default']
28
- LogList: typeof import('./src/components/LogList.vue')['default']
29
- }
30
- export interface ComponentCustomProperties {
31
- vLoading: typeof import('element-plus/es')['ElLoadingDirective']
32
- }
33
- }
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Zen-GitSync - Git同步工具</title>
8
- </head>
9
- <body>
10
- <div id="app"></div>
11
- <script type="module" src="/src/main.ts"></script>
12
- </body>
13
- </html>
@@ -1,28 +0,0 @@
1
- {
2
- "name": "client",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vue-tsc -b && vite build",
9
- "preview": "vite preview"
10
- },
11
- "dependencies": {
12
- "@gitgraph/js": "^1.4.0",
13
- "element-plus": "^2.9.9",
14
- "pinia": "^3.0.2",
15
- "socket.io-client": "^4.8.1",
16
- "vue": "^3.5.13"
17
- },
18
- "devDependencies": {
19
- "@vitejs/plugin-vue": "^5.2.2",
20
- "@vue/tsconfig": "^0.7.0",
21
- "rollup-plugin-visualizer": "^5.14.0",
22
- "typescript": "~5.7.2",
23
- "unplugin-auto-import": "^19.2.0",
24
- "unplugin-vue-components": "^28.5.0",
25
- "vite": "^6.3.1",
26
- "vue-tsc": "^2.2.8"
27
- }
28
- }
@@ -1,27 +0,0 @@
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>
@@ -1,27 +0,0 @@
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>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>