zen-gitsync 2.16.13 → 2.16.15
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/config.js +52 -19
- package/src/ui/public/assets/AgentView-seyN__fu.js +4 -0
- package/src/ui/public/assets/AppVersionBadge-CnWL0hFX.js +2 -0
- package/src/ui/public/assets/BranchSelector-7IbUHOcF.js +1 -0
- package/src/ui/public/assets/CommitForm-DZa67HGc.js +6 -0
- package/src/ui/public/assets/EditorView-JJsAc5Px.js +0 -0
- package/src/ui/public/assets/{FlowExecutionViewer-BblOVK2I.js → FlowExecutionViewer-BbgVCXwD.js} +1 -1
- package/src/ui/public/assets/FlowOrchestrationWorkspace-VRKXC4BW.js +21 -0
- package/src/ui/public/assets/LogList-BNAx53Pd.js +15 -0
- package/src/ui/public/assets/MindmapView-BaOPrXCt.js +2 -0
- package/src/ui/public/assets/MonitorView-BY-vMlOx.js +1 -0
- package/src/ui/public/assets/ProjectStartupButton-DE584iOv.js +1 -0
- package/src/ui/public/assets/{RemoteRepoCard-oN1X-RTu.js → RemoteRepoCard-DbNKpdZg.js} +2 -2
- package/src/ui/public/assets/SourceMapView-DJEerBG6.js +3 -0
- package/src/ui/public/assets/UserInputNode-BhRkpHhB.js +1 -0
- package/src/ui/public/assets/WorkbenchView-B0v0Hzvq.js +28 -0
- package/src/ui/public/assets/{element-plus-C0loJL0J.js → element-plus-tlkbQ0I3.js} +1 -1
- package/src/ui/public/assets/{flow-mindmap-CGn6wqsF.js → flow-mindmap-Cm36aorg.js} +12 -12
- package/src/ui/public/assets/index-B8NvO42m.js +82 -0
- package/src/ui/public/assets/index-BVnb4Q7g.css +1 -0
- package/src/ui/public/assets/{vendor-pbWglt5U.js → vendor-DjLepuxR.js} +8 -8
- package/src/ui/public/assets/vue-flow-CZY2uUK0.js +39 -0
- package/src/ui/public/index.html +5 -5
- package/src/ui/server/routes/fileOpen.js +142 -2
- package/src/ui/server/routes/git.js +36 -5
- package/src/ui/server/routes/git.test.js +147 -0
- package/src/ui/server/routes/gitOps.js +107 -3
- package/src/ui/public/assets/AgentView-DpHVftKB.js +0 -4
- package/src/ui/public/assets/AppVersionBadge-DoBF_YRQ.js +0 -2
- package/src/ui/public/assets/BranchSelector-BG5-5Rji.js +0 -1
- package/src/ui/public/assets/CommitForm-CRvh9bMD.js +0 -6
- package/src/ui/public/assets/EditorView-D8Rj7g4x.js +0 -0
- package/src/ui/public/assets/FlowOrchestrationWorkspace-DYJtdPCz.js +0 -21
- package/src/ui/public/assets/LogList-BJDC-tvx.js +0 -15
- package/src/ui/public/assets/MindmapView-DVKkHt-H.js +0 -2
- package/src/ui/public/assets/MonitorView-ZHUyTSjI.js +0 -1
- package/src/ui/public/assets/ProjectStartupButton-CRpmOPMY.js +0 -1
- package/src/ui/public/assets/SourceMapView-DWjOj5HG.js +0 -3
- package/src/ui/public/assets/UserInputNode-DUS9U0qM.js +0 -1
- package/src/ui/public/assets/WorkbenchView-qCRoJV-3.js +0 -28
- package/src/ui/public/assets/index-DLfjXhwQ.js +0 -82
- package/src/ui/public/assets/index-Fbjp0DKn.css +0 -1
- package/src/ui/public/assets/vue-flow-DRwHh4gM.js +0 -39
|
@@ -18,6 +18,105 @@ import path from 'path';
|
|
|
18
18
|
import open from 'open';
|
|
19
19
|
import { spawn, spawnSync } from 'child_process';
|
|
20
20
|
|
|
21
|
+
const TOOL_INSTALL_PACKAGES = Object.freeze({
|
|
22
|
+
claude: '@anthropic-ai/claude-code',
|
|
23
|
+
codex: '@openai/codex',
|
|
24
|
+
opencode: 'opencode-ai',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const TOOL_DOCS_URLS = Object.freeze({
|
|
28
|
+
vscode: 'https://code.visualstudio.com/download',
|
|
29
|
+
claude: 'https://docs.anthropic.com/en/docs/claude-code/getting-started',
|
|
30
|
+
codex: 'https://help.openai.com/en/articles/11096431-openai-codex-cli-getting-started',
|
|
31
|
+
opencode: 'https://opencode.ai/docs',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function commandExists(command, platform = process.platform) {
|
|
35
|
+
const checker = platform === 'win32' ? 'where.exe' : 'which';
|
|
36
|
+
const result = spawnSync(checker, [command], {
|
|
37
|
+
stdio: 'ignore',
|
|
38
|
+
windowsHide: true,
|
|
39
|
+
});
|
|
40
|
+
return result.status === 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 返回当前平台可展示、可执行的安装方案。
|
|
45
|
+
* executable/args 只在服务端使用;前端只能提交固定 tool id,不能提交命令。
|
|
46
|
+
*/
|
|
47
|
+
export function getToolInstallers(platform = process.platform, hasCommand = commandExists) {
|
|
48
|
+
const npmExecutable = platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
49
|
+
const npmAvailable = hasCommand('npm', platform);
|
|
50
|
+
const installers = {};
|
|
51
|
+
|
|
52
|
+
for (const [tool, packageName] of Object.entries(TOOL_INSTALL_PACKAGES)) {
|
|
53
|
+
installers[tool] = {
|
|
54
|
+
supported: npmAvailable,
|
|
55
|
+
command: `npm install -g ${packageName}`,
|
|
56
|
+
packageManager: 'npm',
|
|
57
|
+
docsUrl: TOOL_DOCS_URLS[tool],
|
|
58
|
+
note: npmAvailable
|
|
59
|
+
? '将在新终端中执行全局安装。安装过程中可能需要登录或确认权限。'
|
|
60
|
+
: '未检测到 npm,请先安装 Node.js/npm,或按照官方文档手动安装。',
|
|
61
|
+
executable: npmExecutable,
|
|
62
|
+
args: ['install', '-g', packageName],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (platform === 'win32') {
|
|
67
|
+
const wingetAvailable = hasCommand('winget', platform);
|
|
68
|
+
installers.vscode = {
|
|
69
|
+
supported: wingetAvailable,
|
|
70
|
+
command: 'winget install --id Microsoft.VisualStudioCode -e --accept-package-agreements --accept-source-agreements',
|
|
71
|
+
packageManager: 'winget',
|
|
72
|
+
docsUrl: TOOL_DOCS_URLS.vscode,
|
|
73
|
+
note: wingetAvailable
|
|
74
|
+
? '将在新终端中通过 winget 安装 Visual Studio Code。'
|
|
75
|
+
: '未检测到 winget,请使用 VS Code 官方安装程序。',
|
|
76
|
+
executable: 'winget',
|
|
77
|
+
args: ['install', '--id', 'Microsoft.VisualStudioCode', '-e', '--accept-package-agreements', '--accept-source-agreements'],
|
|
78
|
+
};
|
|
79
|
+
} else if (platform === 'darwin') {
|
|
80
|
+
const brewAvailable = hasCommand('brew', platform);
|
|
81
|
+
installers.vscode = {
|
|
82
|
+
supported: brewAvailable,
|
|
83
|
+
command: 'brew install --cask visual-studio-code',
|
|
84
|
+
packageManager: 'Homebrew',
|
|
85
|
+
docsUrl: TOOL_DOCS_URLS.vscode,
|
|
86
|
+
note: brewAvailable
|
|
87
|
+
? '将在新终端中通过 Homebrew 安装 Visual Studio Code。'
|
|
88
|
+
: '未检测到 Homebrew,请使用 VS Code 官方安装程序。',
|
|
89
|
+
executable: 'brew',
|
|
90
|
+
args: ['install', '--cask', 'visual-studio-code'],
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
const snapAvailable = hasCommand('snap', platform);
|
|
94
|
+
installers.vscode = {
|
|
95
|
+
supported: snapAvailable,
|
|
96
|
+
command: 'sudo snap install code --classic',
|
|
97
|
+
packageManager: 'snap',
|
|
98
|
+
docsUrl: TOOL_DOCS_URLS.vscode,
|
|
99
|
+
note: snapAvailable
|
|
100
|
+
? '将在新终端中通过 snap 安装 Visual Studio Code,可能需要输入系统密码。'
|
|
101
|
+
: '当前 Linux 环境未检测到 snap,请按照发行版对应的官方说明安装。',
|
|
102
|
+
executable: 'sudo',
|
|
103
|
+
args: ['snap', 'install', 'code', '--classic'],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return installers;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function publicInstallerInfo(installers) {
|
|
111
|
+
return Object.fromEntries(Object.entries(installers).map(([tool, installer]) => [tool, {
|
|
112
|
+
supported: installer.supported,
|
|
113
|
+
command: installer.command,
|
|
114
|
+
packageManager: installer.packageManager,
|
|
115
|
+
docsUrl: installer.docsUrl,
|
|
116
|
+
note: installer.note,
|
|
117
|
+
}]));
|
|
118
|
+
}
|
|
119
|
+
|
|
21
120
|
function spawnDetached(command, args, options = {}) {
|
|
22
121
|
return new Promise((resolve, reject) => {
|
|
23
122
|
const child = spawn(command, args, {
|
|
@@ -125,6 +224,18 @@ async function launchOpenCode(dirPath) {
|
|
|
125
224
|
return launchInTerminal(dirPath, 'opencode');
|
|
126
225
|
}
|
|
127
226
|
|
|
227
|
+
async function launchToolInstaller(installer, dirPath = process.cwd()) {
|
|
228
|
+
if (process.platform === 'win32') {
|
|
229
|
+
// 安装命令完全来自服务端白名单。使用可见 cmd 窗口,让用户看到进度和错误。
|
|
230
|
+
const commandLine = [installer.executable, ...installer.args].join(' ');
|
|
231
|
+
return spawnDetached('cmd.exe', ['/c', 'start', '', 'cmd.exe', '/k', commandLine], {
|
|
232
|
+
cwd: dirPath,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return launchInTerminal(dirPath, installer.executable, installer.args);
|
|
237
|
+
}
|
|
238
|
+
|
|
128
239
|
export function registerFileOpenRoutes({
|
|
129
240
|
app
|
|
130
241
|
}) {
|
|
@@ -405,6 +516,26 @@ export function registerFileOpenRoutes({
|
|
|
405
516
|
}
|
|
406
517
|
}));
|
|
407
518
|
|
|
519
|
+
// 安装本地工具:前端只能传固定 tool id,实际命令由服务端按平台从白名单选择。
|
|
520
|
+
app.post('/api/install-tool', asyncRoute(async (req, res) => {
|
|
521
|
+
const { tool } = req.body || {};
|
|
522
|
+
const installers = getToolInstallers();
|
|
523
|
+
const installer = installers[tool];
|
|
524
|
+
|
|
525
|
+
if (!installer) {
|
|
526
|
+
throw new HttpError(400, '不支持的工具');
|
|
527
|
+
}
|
|
528
|
+
if (!installer.supported) {
|
|
529
|
+
throw new HttpError(400, installer.note || '当前系统不支持一键安装,请按照官方文档手动安装');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
await launchToolInstaller(installer);
|
|
533
|
+
res.json({
|
|
534
|
+
success: true,
|
|
535
|
+
message: '安装命令已在新终端中启动',
|
|
536
|
+
});
|
|
537
|
+
}));
|
|
538
|
+
|
|
408
539
|
// 检测本地工具是否已安装(供前端根据结果决定是否显示对应按钮)
|
|
409
540
|
// 检测方式: spawn 'tool --version',exit 0 即视为已安装
|
|
410
541
|
// 超时 15s:Claude CLI / opencode 等 Node 包装的工具首次冷启动
|
|
@@ -435,6 +566,15 @@ export function registerFileOpenRoutes({
|
|
|
435
566
|
checkCmd('opencode'),
|
|
436
567
|
]);
|
|
437
568
|
|
|
438
|
-
|
|
569
|
+
const installers = getToolInstallers();
|
|
570
|
+
res.json({
|
|
571
|
+
success: true,
|
|
572
|
+
platform: process.platform,
|
|
573
|
+
installers: publicInstallerInfo(installers),
|
|
574
|
+
vscode,
|
|
575
|
+
claude,
|
|
576
|
+
codex,
|
|
577
|
+
opencode,
|
|
578
|
+
});
|
|
439
579
|
}));
|
|
440
|
-
}
|
|
580
|
+
}
|
|
@@ -95,14 +95,45 @@ export function registerGitRoutes({
|
|
|
95
95
|
if (!branch) {
|
|
96
96
|
throw new HttpError(400, '分支名称不能为空');
|
|
97
97
|
}
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
let finalBranch = branch;
|
|
100
|
+
|
|
101
|
+
// 远程分支(如 origin/xxx):直接 checkout 远程引用会进入 detached HEAD,
|
|
102
|
+
// 需要先解析出同名本地分支——已存在则直接切换,不存在则基于远程分支创建并跟踪。
|
|
103
|
+
if (branch.includes('/')) {
|
|
104
|
+
const { stdout: remoteRef } = await execGitCommand(
|
|
105
|
+
['rev-parse', '--verify', '--quiet', `refs/remotes/${branch}`],
|
|
106
|
+
{ ignoreError: true, log: false }
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
if (remoteRef.trim()) {
|
|
110
|
+
// 去掉第一个路径段(远程名),得到本地分支名,支持 origin/feature/x 这类多级名称
|
|
111
|
+
const localName = branch.substring(branch.indexOf('/') + 1);
|
|
112
|
+
const { stdout: localRef } = await execGitCommand(
|
|
113
|
+
['rev-parse', '--verify', '--quiet', `refs/heads/${localName}`],
|
|
114
|
+
{ ignoreError: true, log: false }
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (localRef.trim()) {
|
|
118
|
+
// 本地已有同名分支,直接切换
|
|
119
|
+
finalBranch = localName;
|
|
120
|
+
} else {
|
|
121
|
+
// 本地没有:创建同名本地分支并跟踪该远程分支
|
|
122
|
+
await execGitCommand(['checkout', '--track', branch]);
|
|
123
|
+
clearBranchCache();
|
|
124
|
+
res.json({ success: true, branch: localName, created: true });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
99
130
|
// 执行分支切换
|
|
100
|
-
await execGitCommand(['checkout',
|
|
101
|
-
|
|
131
|
+
await execGitCommand(['checkout', finalBranch]);
|
|
132
|
+
|
|
102
133
|
// 清除分支缓存,因为分支已切换
|
|
103
134
|
clearBranchCache();
|
|
104
|
-
|
|
105
|
-
res.json({ success: true });
|
|
135
|
+
|
|
136
|
+
res.json({ success: true, branch: finalBranch });
|
|
106
137
|
} catch (error) {
|
|
107
138
|
logger.error('切换分支失败:', error);
|
|
108
139
|
res.status(500).json({ success: false, error: error.message });
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Copyright 2026 xz333221
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
//
|
|
15
|
+
// src/ui/server/routes/git.js 单元测试。
|
|
16
|
+
// 验证 POST /api/checkout 对远程分支的处理:
|
|
17
|
+
// 选中 origin/xxx 时应切到同名本地分支,不存在则 --track 自动创建,
|
|
18
|
+
// 而不是直接 checkout 远程引用进入 detached HEAD。
|
|
19
|
+
//
|
|
20
|
+
// 测试不真起 Express,直接调 registerGitRoutes 注入 mock app。
|
|
21
|
+
import { test } from 'node:test'
|
|
22
|
+
import assert from 'node:assert/strict'
|
|
23
|
+
import { registerGitRoutes } from './git.js'
|
|
24
|
+
|
|
25
|
+
/** 最小 express app mock:只实现 get/post + handler 调用 */
|
|
26
|
+
function makeApp() {
|
|
27
|
+
const handlers = new Map() // key = "METHOD path" → handler
|
|
28
|
+
return {
|
|
29
|
+
get(path, handler) { handlers.set(`GET ${path}`, handler) },
|
|
30
|
+
post(...args) {
|
|
31
|
+
// create-branch 等路由带 express.json() 中间件,handler 永远是最后一个参数
|
|
32
|
+
const path = args[0]
|
|
33
|
+
const handler = args[args.length - 1]
|
|
34
|
+
handlers.set(`POST ${path}`, handler)
|
|
35
|
+
},
|
|
36
|
+
invoke(method, path, req = { query: {}, body: {} }, res = makeRes()) {
|
|
37
|
+
const key = `${method} ${path}`
|
|
38
|
+
const handler = handlers.get(key)
|
|
39
|
+
assert.ok(handler, `no handler for ${key}`)
|
|
40
|
+
return handler(req, res, () => {})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function makeRes() {
|
|
45
|
+
return {
|
|
46
|
+
statusCode: 200,
|
|
47
|
+
payload: null,
|
|
48
|
+
status(c) { this.statusCode = c; return this },
|
|
49
|
+
json(p) { this.payload = p; return this }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 构造注入依赖,calls 记录所有 git 子命令 */
|
|
54
|
+
function makeDeps(app, execGitCommand) {
|
|
55
|
+
const calls = []
|
|
56
|
+
registerGitRoutes({
|
|
57
|
+
app,
|
|
58
|
+
execGitCommand: async (args, opts) => { calls.push(args); return execGitCommand(args, opts) },
|
|
59
|
+
clearBranchCache: () => {}
|
|
60
|
+
})
|
|
61
|
+
return calls
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ========== /api/checkout 远程分支处理 ==========
|
|
65
|
+
|
|
66
|
+
test('checkout: 本地分支 → 直接 checkout,不做远程探测', async () => {
|
|
67
|
+
const app = makeApp()
|
|
68
|
+
const calls = makeDeps(app, async () => ({ stdout: '' }))
|
|
69
|
+
const res = makeRes()
|
|
70
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'develop' } }, res)
|
|
71
|
+
assert.equal(res.statusCode, 200)
|
|
72
|
+
assert.deepEqual(res.payload, { success: true, branch: 'develop' })
|
|
73
|
+
assert.deepEqual(calls, [['checkout', 'develop']])
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('checkout: 远程分支 + 本地已存在同名分支 → 切到本地分支', async () => {
|
|
77
|
+
const app = makeApp()
|
|
78
|
+
const calls = makeDeps(app, async (args) => {
|
|
79
|
+
if (args[0] === 'rev-parse' && args.includes('refs/remotes/origin/release-v1')) return { stdout: 'abc123\n' }
|
|
80
|
+
if (args[0] === 'rev-parse' && args.includes('refs/heads/release-v1')) return { stdout: 'def456\n' }
|
|
81
|
+
return { stdout: '' }
|
|
82
|
+
})
|
|
83
|
+
const res = makeRes()
|
|
84
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/release-v1' } }, res)
|
|
85
|
+
assert.equal(res.statusCode, 200)
|
|
86
|
+
assert.deepEqual(res.payload, { success: true, branch: 'release-v1' })
|
|
87
|
+
// 最后一步必须是 checkout 本地分支名,而不是 checkout origin/release-v1(detached HEAD)
|
|
88
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', 'release-v1'])
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('checkout: 远程分支 + 本地无同名分支 → checkout --track 自动创建', async () => {
|
|
92
|
+
const app = makeApp()
|
|
93
|
+
const calls = makeDeps(app, async (args) => {
|
|
94
|
+
if (args[0] === 'rev-parse' && args.includes('refs/remotes/origin/release-v1')) return { stdout: 'abc123\n' }
|
|
95
|
+
if (args[0] === 'rev-parse' && args.includes('refs/heads/release-v1')) return { stdout: '' } // 本地不存在
|
|
96
|
+
return { stdout: '' }
|
|
97
|
+
})
|
|
98
|
+
const res = makeRes()
|
|
99
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/release-v1' } }, res)
|
|
100
|
+
assert.equal(res.statusCode, 200)
|
|
101
|
+
assert.deepEqual(res.payload, { success: true, branch: 'release-v1', created: true })
|
|
102
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', '--track', 'origin/release-v1'])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('checkout: 多级远程分支(origin/feature/x)→ 本地名取第一个斜杠之后部分', async () => {
|
|
106
|
+
const app = makeApp()
|
|
107
|
+
const calls = makeDeps(app, async (args) => {
|
|
108
|
+
if (args[0] === 'rev-parse' && args.includes('refs/remotes/origin/feature/x')) return { stdout: 'abc123\n' }
|
|
109
|
+
if (args[0] === 'rev-parse' && args.includes('refs/heads/feature/x')) return { stdout: '' }
|
|
110
|
+
return { stdout: '' }
|
|
111
|
+
})
|
|
112
|
+
const res = makeRes()
|
|
113
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/feature/x' } }, res)
|
|
114
|
+
assert.equal(res.statusCode, 200)
|
|
115
|
+
assert.deepEqual(res.payload, { success: true, branch: 'feature/x', created: true })
|
|
116
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', '--track', 'origin/feature/x'])
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('checkout: 带斜杠但远程引用不存在(如本地 feature/x)→ 按原名直接 checkout', async () => {
|
|
120
|
+
const app = makeApp()
|
|
121
|
+
const calls = makeDeps(app, async (args) => {
|
|
122
|
+
if (args[0] === 'rev-parse') return { stdout: '' } // refs/remotes/feature/x 不存在
|
|
123
|
+
return { stdout: '' }
|
|
124
|
+
})
|
|
125
|
+
const res = makeRes()
|
|
126
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'feature/x' } }, res)
|
|
127
|
+
assert.equal(res.statusCode, 200)
|
|
128
|
+
assert.deepEqual(res.payload, { success: true, branch: 'feature/x' })
|
|
129
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', 'feature/x'])
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('checkout: rev-parse 探测抛错 → ignoreError 吞下,按原名 checkout(非 500)', async () => {
|
|
133
|
+
const app = makeApp()
|
|
134
|
+
const calls = makeDeps(app, async (args, opts) => {
|
|
135
|
+
if (args[0] === 'rev-parse') {
|
|
136
|
+
const err = new Error('Command failed: git rev-parse --verify --quiet refs/remotes/origin/gone')
|
|
137
|
+
err.code = 128
|
|
138
|
+
if (opts && opts.ignoreError) return { stdout: '' }
|
|
139
|
+
throw err
|
|
140
|
+
}
|
|
141
|
+
return { stdout: '' }
|
|
142
|
+
})
|
|
143
|
+
const res = makeRes()
|
|
144
|
+
await app.invoke('POST', '/api/checkout', { query: {}, body: { branch: 'origin/gone' } }, res)
|
|
145
|
+
assert.equal(res.statusCode, 200, '探测失败应降级为普通 checkout,不应 500')
|
|
146
|
+
assert.deepEqual(calls[calls.length - 1], ['checkout', 'origin/gone'])
|
|
147
|
+
})
|
|
@@ -36,7 +36,15 @@ export function registerGitOpsRoutes({
|
|
|
36
36
|
setRecentPushStatus
|
|
37
37
|
}) {
|
|
38
38
|
// 提交更改
|
|
39
|
+
// 锁定文件排除逻辑:提交前检测暂存区中是否有锁定文件,
|
|
40
|
+
// 如果有则临时 git reset HEAD 取消暂存 → 提交 → 恢复暂存状态。
|
|
41
|
+
// 这样用户可以从左侧文件列表区域自由操作锁定文件(暂存/撤回),
|
|
42
|
+
// 但右侧提交时锁定文件不会被提交。
|
|
39
43
|
app.post('/api/commit', express.json(), async (req, res) => {
|
|
44
|
+
let tempFile = null;
|
|
45
|
+
// 临时取消暂存的锁定文件列表(提交后需恢复)
|
|
46
|
+
let unstagedLockedFiles = [];
|
|
47
|
+
|
|
40
48
|
try {
|
|
41
49
|
const { message, hasNewlines, noVerify } = req.body;
|
|
42
50
|
|
|
@@ -44,7 +52,6 @@ export function registerGitOpsRoutes({
|
|
|
44
52
|
const commitArgs = ['commit'];
|
|
45
53
|
|
|
46
54
|
// 如果消息包含换行符,使用文件方式提交
|
|
47
|
-
let tempFile
|
|
48
55
|
if (hasNewlines) {
|
|
49
56
|
// 创建临时文件存储提交信息
|
|
50
57
|
tempFile = path.join(os.tmpdir(), `commit-msg-${Date.now()}.txt`);
|
|
@@ -60,17 +67,114 @@ export function registerGitOpsRoutes({
|
|
|
60
67
|
commitArgs.push('--no-verify');
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
// ====== 锁定文件排除逻辑 ======
|
|
71
|
+
const lockedFiles = await configManager.getLockedFiles();
|
|
72
|
+
let skippedLockedFiles = [];
|
|
73
|
+
|
|
74
|
+
if (lockedFiles.length > 0) {
|
|
75
|
+
// 获取当前暂存区文件列表
|
|
76
|
+
const { stdout: stagedOutput } = await execGitCommand(
|
|
77
|
+
['diff', '--cached', '--name-only'], { log: false }
|
|
78
|
+
);
|
|
79
|
+
const stagedFiles = stagedOutput.split('\n').filter(Boolean);
|
|
80
|
+
|
|
81
|
+
if (stagedFiles.length > 0) {
|
|
82
|
+
// 获取 Git 根目录,用于将锁定文件的绝对路径转为 git 相对路径
|
|
83
|
+
let gitRoot;
|
|
84
|
+
try {
|
|
85
|
+
const { stdout: rootOut } = await execGitCommand(
|
|
86
|
+
['rev-parse', '--show-toplevel'], { log: false }
|
|
87
|
+
);
|
|
88
|
+
gitRoot = path.normalize(rootOut.trim());
|
|
89
|
+
} catch (_) {
|
|
90
|
+
gitRoot = path.normalize(process.cwd());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 将锁定文件路径统一为 git 相对路径(正斜杠)以便与
|
|
94
|
+
// git diff --cached --name-only 的输出进行比较
|
|
95
|
+
function toGitRelative(p) {
|
|
96
|
+
const normalized = path.normalize(p);
|
|
97
|
+
const relative = (path.isAbsolute(normalized) && normalized.startsWith(gitRoot))
|
|
98
|
+
? path.relative(gitRoot, normalized)
|
|
99
|
+
: normalized;
|
|
100
|
+
return relative.replace(/\\/g, '/');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const lockedRelativeSet = new Set(lockedFiles.map(toGitRelative));
|
|
104
|
+
|
|
105
|
+
// 找出暂存区中的锁定文件
|
|
106
|
+
unstagedLockedFiles = stagedFiles.filter(
|
|
107
|
+
f => lockedRelativeSet.has(f.replace(/\\/g, '/'))
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (unstagedLockedFiles.length > 0) {
|
|
111
|
+
// 临时取消暂存锁定文件
|
|
112
|
+
await execGitCommand(
|
|
113
|
+
['reset', 'HEAD', '--', ...unstagedLockedFiles], { log: false }
|
|
114
|
+
);
|
|
115
|
+
skippedLockedFiles = unstagedLockedFiles;
|
|
116
|
+
|
|
117
|
+
// 检查取消暂存后是否还有可提交的内容
|
|
118
|
+
const { stdout: remainingStaged } = await execGitCommand(
|
|
119
|
+
['diff', '--cached', '--name-only'], { log: false }
|
|
120
|
+
);
|
|
121
|
+
if (!remainingStaged.trim()) {
|
|
122
|
+
// 所有暂存文件都是锁定的 — 恢复暂存状态并返回提示
|
|
123
|
+
await execGitCommand(
|
|
124
|
+
['add', '--', ...unstagedLockedFiles], { log: false }
|
|
125
|
+
);
|
|
126
|
+
if (tempFile) {
|
|
127
|
+
await fs.unlink(tempFile).catch(() => {});
|
|
128
|
+
}
|
|
129
|
+
res.json({
|
|
130
|
+
success: false,
|
|
131
|
+
error: '所有暂存文件都被锁定,没有可提交的内容',
|
|
132
|
+
skippedLockedFiles,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
63
140
|
logger.info(`commitArgs ==>`, commitArgs);
|
|
64
141
|
// 执行提交命令
|
|
65
142
|
await execGitCommand(commitArgs);
|
|
66
143
|
|
|
67
144
|
// 如果使用了临时文件,删除它
|
|
68
|
-
if (
|
|
145
|
+
if (tempFile) {
|
|
69
146
|
await fs.unlink(tempFile).catch(() => {});
|
|
70
147
|
}
|
|
71
148
|
|
|
72
|
-
|
|
149
|
+
// 恢复锁定文件的暂存状态(保留用户在左侧文件列表中的暂存操作)
|
|
150
|
+
if (unstagedLockedFiles.length > 0) {
|
|
151
|
+
try {
|
|
152
|
+
await execGitCommand(
|
|
153
|
+
['add', '--', ...unstagedLockedFiles], { log: false }
|
|
154
|
+
);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
logger.warn('恢复锁定文件暂存状态失败:', e.message);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
res.json({
|
|
161
|
+
success: true,
|
|
162
|
+
skippedLockedFiles: skippedLockedFiles.length > 0
|
|
163
|
+
? skippedLockedFiles
|
|
164
|
+
: undefined,
|
|
165
|
+
});
|
|
73
166
|
} catch (error) {
|
|
167
|
+
// 出错时也要尝试恢复锁定文件的暂存状态
|
|
168
|
+
if (unstagedLockedFiles.length > 0) {
|
|
169
|
+
try {
|
|
170
|
+
await execGitCommand(
|
|
171
|
+
['add', '--', ...unstagedLockedFiles], { log: false }
|
|
172
|
+
);
|
|
173
|
+
} catch (_) { /* 忽略恢复失败,不掩盖原始错误 */ }
|
|
174
|
+
}
|
|
175
|
+
if (tempFile) {
|
|
176
|
+
await fs.unlink(tempFile).catch(() => {});
|
|
177
|
+
}
|
|
74
178
|
res.status(500).json({ success: false, error: error.message });
|
|
75
179
|
}
|
|
76
180
|
});
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-BM3Ffeng.js";import{Bt as e,Cn as t,Et as n,G as r,Jn as i,Ln as a,Mn as o,Pt as s,Tn as c,Vn as l,Xn as u,Y as d,Zn as f,_n as p,_r as m,a as h,ar as g,et as _,gr as v,hn as y,ht as b,i as x,ln as S,lt as C,mn as w,mr as T,o as E,on as D,sr as O,tn as k,un as A,vn as j,zn as M}from"./element-plus-C0loJL0J.js";import{i as N,n as P,o as ee}from"./vendor-pbWglt5U.js";import{E as F,T as I,x as L}from"./index-DLfjXhwQ.js";var R=64*1024;function z(e){if(!e||!Array.isArray(e.messages))return[];let t=[],n=e.messages.length;for(let r=0;r<n;r++){let i=e.messages[r];if(i.role!==`system`){if(i.role===`user`){let e=``,n;if(typeof i.content==`string`)e=i.content;else if(Array.isArray(i.content)){e=i.content.filter(e=>e?.type===`text`).map(e=>e.text||``).join(` `);let t=i.content.filter(e=>e?.type===`image_url`&&typeof e.image_url?.url==`string`).map((e,t)=>({id:`att-${r}-${t}`,name:`image`,size:0,type:H(e.image_url?.url||``)||`image/*`,preview:e.image_url?.url}));t.length>0&&(n=t)}(e||n)&&t.push({id:`msg-${r}`,role:`user`,content:e,attachments:n,status:`done`,createdAt:Date.now()})}else if(i.role===`assistant`){let a=``;typeof i.content==`string`&&(a=i.content),a.length>R&&(a=`…(前文已截断)\n${a.slice(-R)}`);let o=[];if(i.tool_calls&&i.tool_calls.length>0)for(let e of i.tool_calls)o.push({id:e.id||e.function?.name||N(),name:e.function?.name||``,arguments:e.function?.arguments||``,argsPreview:B(e.function?.name||``,e.function?.arguments||``),status:`done`,result:``});let s=r+1;for(;s<n&&e.messages[s].role===`tool`;){let t=e.messages[s],n=t.tool_call_id||t.name||``,i=o.find(e=>e.id===n);if(i){let e=t.content;typeof e!=`string`&&(e=`(non-text result)`),e.length>R&&(e=`…(前文已截断)\n${e.slice(-R)}`),i.result=e}r=s,s++}let c=!!a,l=o.length>0;(c||l)&&t.push({id:`msg-${r}`,role:`assistant`,content:a||``,toolCalls:l?o:void 0,status:`done`,createdAt:Date.now()})}}}return t}function B(e,t){try{let n=JSON.parse(t);switch(e){case`run_command`:return String(n.command||``).slice(0,200);case`read_file`:case`write_file`:case`edit_file`:return String(n.path||``);case`list_files`:return String(n.path||`.`);case`search_text`:return String(n.pattern||``);default:return JSON.stringify(n).slice(0,200)}}catch{return t.slice(0,200)}}function V(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>t(String(r.result||``)),r.onerror=()=>n(r.error||Error(`FileReader failed`)),r.readAsDataURL(e)})}function H(e){let t=/^data:([^;,]+)/.exec(e);return t?t[1]:``}function U(){let e=O([]),t=O(!1),n=O(null),r=O([]),i=O(!1),a=O(!1),o=null,s=0;async function c(){t.value=!0;try{let t=await fetch(`/api/agent/sessions`).then(e=>e.json());if(!t.success){h.error(t.error||F(`@AGENT:加载会话列表失败`));return}e.value=Array.isArray(t.sessions)?t.sessions:[]}catch(e){h.error(F(`@AGENT:加载会话列表失败`)+`: `+(e?.message||e))}finally{t.value=!1}}async function l(e){a.value=!0,n.value=e;try{let t=await fetch(`/api/agent/sessions/${encodeURIComponent(e)}`).then(e=>e.json());if(!t.success){h.error(t.error||F(`@AGENT:加载会话失败`));return}r.value=z(t.session)}catch(e){h.error(F(`@AGENT:加载会话失败`)+`: `+(e?.message||e))}finally{a.value=!1}}async function u(t){try{await x.confirm(F(`@AGENT:确认删除该会话`),F(`@AGENT:删除后无法恢复`),{type:`warning`})}catch{return}try{let i=await fetch(`/api/agent/sessions/${encodeURIComponent(t)}`,{method:`DELETE`}).then(e=>e.json());if(!i.success){h.error(i.error||F(`@AGENT:删除失败`));return}e.value=e.value.filter(e=>e.sessionId!==t),n.value===t&&(n.value=null,r.value=[]),h.success(F(`@AGENT:已删除`))}catch(e){h.error(F(`@AGENT:删除失败`)+`: `+(e?.message||e))}}async function d(t,n){try{let r=await fetch(`/api/agent/sessions/${encodeURIComponent(t)}`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({title:n})}).then(e=>e.json());if(!r.success){h.error(r.error||F(`@AGENT:重命名失败`));return}let i=e.value.find(e=>e.sessionId===t);i&&(i.title=n),h.success(F(`@AGENT:已重命名`))}catch(e){h.error(F(`@AGENT:重命名失败`)+`: `+(e?.message||e))}}function f(){n.value=null,r.value=[]}async function p(e,t=[]){let a=t.filter(e=>e?.file?.type?.startsWith(`image/`));if(a.length<t.length&&h.warning(F(`@AGENT:仅支持发送图片,非图片文件已忽略`)),!e.trim()&&a.length===0||i.value)return;let l=(await Promise.allSettled(a.map(e=>V(e.file)))).map(e=>e.status===`fulfilled`?e.value:``).filter(e=>e.startsWith(`data:image/`)),u=++s;o=new AbortController;let d=o,f={id:N(),role:`user`,content:e,status:`done`,createdAt:Date.now()};a.length>0&&(f.attachments=a.map(e=>({id:e.id,name:e.file.name||`image`,size:e.file.size||0,type:e.file.type||``,preview:e.preview}))),r.value.push(f);let p=g({id:N(),role:`assistant`,content:``,status:`pending`,createdAt:Date.now()});r.value.push(p),i.value=!0;let m=[];try{let t=await fetch(`/api/agent/chat`,{method:`POST`,headers:{"Content-Type":`application/json`,Accept:`text/event-stream`},body:JSON.stringify({sessionId:n.value||``,userMessage:e,...l.length>0?{images:l}:{}}),signal:d.signal});if(u!==s)return;if(!t.ok||!t.body){let e=await t.text().catch(()=>``);throw Error(e||`HTTP ${t.status}`)}let i=t.body.getReader(),a=new TextDecoder(`utf-8`),o=``,f=n.value;for(;;){let{value:e,done:t}=await i.read();if(u!==s)return;if(t)break;o+=a.decode(e,{stream:!0});let c=o.split(`
|
|
2
|
-
`);o=c.pop()??``;for(let e of c){let t=e.trim();if(!t.startsWith(`data:`))continue;let r=t.slice(5).trim();if(!r)continue;let i;try{i=JSON.parse(r)}catch{continue}switch(i.type){case`meta`:i.sessionId&&(f=i.sessionId,n.value=i.sessionId);break;case`thinking`:p.reasoning||(p.reasoning=``,p.reasoningStatus=`streaming`),p.reasoning+=String(i.delta||``),p.status=`streaming`;break;case`content`:p.status===`pending`&&(p.status=`streaming`);{let e=String(i.delta||``);p.content+=e;let t=P(p.content);t.reasoning&&(p.reasoning||(p.reasoning=``,p.reasoningStatus=`streaming`),p.reasoning=t.reasoning,p.content=t.content)}break;case`tool_call_start`:{let e=g({id:i.toolCallId||N(),name:i.name||``,argsPreview:i.argsPreview||``,arguments:i.argsPreview||``,status:`running`,result:``});m.push(e),p.toolCalls||=[],p.toolCalls.push(e),p.status===`pending`&&(p.status=`streaming`);break}case`tool_result`:{let e=m.find(e=>e.id===i.toolCallId);e&&(e.result=String(i.result||``),e.status=`done`);break}case`done`:{let e=P(i.content||p.content);e.reasoning&&!p.reasoning&&(p.reasoning=e.reasoning),p.content=e.content,p.status=`done`,p.reasoningStatus===`streaming`&&(p.reasoningStatus=`done`);break}case`error`:p.status=`error`,p.error=String(i.error||F(`@AGENT:未知错误`)),p.reasoningStatus===`streaming`&&(p.reasoningStatus=`done`);break}}r.value=[...r.value]}if(u!==s)return;p.status===`pending`&&(p.status=`done`),f&&f!==n.value&&(n.value=f),c().catch(()=>{})}catch(e){if(u!==s)return;e?.name===`AbortError`||d.signal.aborted?(p.content=(p.content||``)+`
|
|
3
|
-
|
|
4
|
-
[`+F(`@AGENT:已停止`)+`]`,p.status=`done`,p.reasoningStatus===`streaming`&&(p.reasoningStatus=`done`)):(p.status=`error`,p.error=e?.message||String(e),h.error(p.error||F(`@AGENT:对话失败`)))}finally{u===s&&(i.value=!1,o=null)}}function m(){o&&=(o.abort(),null)}return{sessions:e,sessionsLoading:t,currentSessionId:n,messages:r,isStreaming:i,sessionLoading:a,loadSessions:c,loadSession:l,deleteSession:u,renameSession:d,newSession:f,sendMessage:p,stop:m}}var W=c({__name:`AgentView`,setup(t,{expose:c}){c();let l=L(),{sessions:u,sessionsLoading:f,currentSessionId:p,messages:m,isStreaming:g,sessionLoading:v,loadSessions:y,loadSession:S,deleteSession:T,renameSession:E,newSession:D,sendMessage:k,stop:A}=U(),j=w(()=>{let e=l.theme;return e===`dark`||e===`auto`&&window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}),M=O(``),N=w(()=>{if(!M.value.trim())return u.value;let e=M.value.toLowerCase();return u.value.filter(t=>(t.title||``).toLowerCase().includes(e)||(t.model||``).toLowerCase().includes(e))}),P=w(()=>[{id:`p1`,label:F(`@AGENT:查看项目结构`),prompt:F(`@AGENT:prompt_p1`)},{id:`p2`,label:F(`@AGENT:分析代码质量`),prompt:F(`@AGENT:prompt_p2`)},{id:`p3`,label:F(`@AGENT:帮我写测试`),prompt:F(`@AGENT:prompt_p3`)},{id:`p4`,label:F(`@AGENT:Git 状态检查`),prompt:F(`@AGENT:prompt_p4`)}]);async function I(e){await k(e.text,e.files),await o(),B()}async function R(e){await k(e.prompt),await o(),B()}let z=O(null);function B(e=!0){z.value?.scrollToBottom(e)}i(()=>m.value.length,()=>{o(()=>B(!1))});function V(e){if(g.value){h.warning(F(`@AGENT:请先停止当前生成`));return}S(e)}function H(){if(g.value){h.warning(F(`@AGENT:请先停止当前生成`));return}D()}function W(e,t){if(t.stopPropagation(),g.value&&p.value===e){h.warning(F(`@AGENT:请先停止当前生成`));return}T(e)}async function G(e,t,n){n.stopPropagation();try{let{value:n}=await x.prompt(F(`@AGENT:请输入新的会话标题`),F(`@AGENT:重命名会话`),{inputValue:t,inputPattern:/.+/,inputErrorMessage:F(`@AGENT:标题不能为空`)});n&&n!==t&&await E(e,n)}catch{}}function K(e){if(!e)return``;try{let t=new Date(e);if(Number.isNaN(t.getTime()))return e;let n=new Date,r=n.getTime()-t.getTime(),i=e=>String(e).padStart(2,`0`);if(t.toDateString()===n.toDateString())return`${i(t.getHours())}:${i(t.getMinutes())}`;if(r<10080*60*1e3){let e=Math.floor(r/(1440*60*1e3));return e===0?F(`@AGENT:今天`):`${e}${F(`@AGENT:天前`)}`}return`${t.getFullYear()}-${i(t.getMonth()+1)}-${i(t.getDate())}`}catch{return e}}let q=O(280),J=O(!1);function Y(e){J.value=!0;let t=e.clientX,n=q.value,r=e=>{let r=e.clientX-t;q.value=Math.max(200,Math.min(500,n+r))},i=()=>{J.value=!1,document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)}a(()=>{y()});let X={configStore:l,sessions:u,sessionsLoading:f,currentSessionId:p,messages:m,isStreaming:g,sessionLoading:v,loadSessions:y,loadSession:S,deleteSession:T,renameSession:E,newSession:D,sendMessage:k,stop:A,chatTheme:j,searchQuery:M,filteredSessions:N,presetQuestions:P,onSend:I,onSelectPreset:R,chatContainerRef:z,scrollToBottom:B,selectSession:V,handleNewSession:H,handleDelete:W,handleRename:G,formatDate:K,sidebarWidth:q,isResizing:J,startResize:Y,get $t(){return F},get ElTooltip(){return r},get ElIcon(){return d},get Plus(){return s},get Search(){return e},get Delete(){return C},get Edit(){return b},get ChatLineRound(){return _},get Loading(){return n},get ChatContainer(){return ee}};return Object.defineProperty(X,`__isScriptSetup`,{enumerable:!1,value:!0}),X}}),G={class:`agent-view`},K={class:`sidebar-header`},q={class:`sidebar-search`},J=[`placeholder`],Y={class:`session-list`},X={key:0,class:`empty-state`},Z={class:`empty-text`},Q={key:0,class:`empty-hint`},te=[`onClick`],ne={class:`session-item-main`},$={class:`session-item-title`},re={class:`session-item-meta`},ie={class:`meta-time`},ae={class:`meta-count`},oe={key:0,class:`meta-source cli`},se=[`onClick`],ce=[`onClick`],le={class:`agent-chat-area`},ue={key:0,class:`chat-loading`},de={key:0,class:`stop-button-bar`};function fe(e,n,r,i,a,o){let s=E;return M(),j(`div`,G,[p(` ═══ 左侧:会话列表 ═══ `),y(`aside`,{class:`agent-sidebar`,style:v({width:i.sidebarWidth+`px`})},[p(` 顶部操作栏 `),y(`div`,K,[y(`button`,{class:`new-session-btn`,onClick:i.handleNewSession},[t(i.ElIcon,null,{default:u(()=>[t(i.Plus)]),_:1}),y(`span`,null,m(i.$t(`@AGENT:新建会话`)),1)])]),p(` 搜索框 `),y(`div`,q,[t(i.ElIcon,{class:`search-icon`},{default:u(()=>[t(i.Search)]),_:1}),f(y(`input`,{"onUpdate:modelValue":n[0]||=e=>i.searchQuery=e,placeholder:i.$t(`@AGENT:搜索会话...`),class:`search-input`,type:`text`},null,8,J),[[D,i.searchQuery]])]),p(` 会话列表 `),f((M(),j(`div`,Y,[i.filteredSessions.length===0&&!i.sessionsLoading?(M(),j(`div`,X,[t(i.ElIcon,{size:28,color:`var(--text-tertiary)`},{default:u(()=>[t(i.ChatLineRound)]),_:1}),y(`div`,Z,m(i.searchQuery?i.$t(`@AGENT:未找到匹配的会话`):i.$t(`@AGENT:暂无会话`)),1),i.searchQuery?p(`v-if`,!0):(M(),j(`div`,Q,m(i.$t(`@AGENT:点击上方按钮开始对话`)),1))])):p(`v-if`,!0),(M(!0),j(A,null,l(i.filteredSessions,e=>(M(),j(`div`,{key:e.sessionId,class:T([`session-item`,{active:i.currentSessionId===e.sessionId}]),onClick:t=>i.selectSession(e.sessionId)},[y(`div`,ne,[y(`div`,$,m(e.title||i.$t(`@AGENT:无标题`)),1),y(`div`,re,[y(`span`,ie,m(i.formatDate(e.updatedAt)),1),n[3]||=y(`span`,{class:`meta-dot`},`·`,-1),y(`span`,ae,m(e.messageCount)+` `+m(i.$t(`@AGENT:条`)),1),e.source===`cli`?(M(),j(`span`,oe,`CLI`)):p(`v-if`,!0)])]),y(`div`,{class:`session-item-actions`,onClick:n[1]||=S(()=>{},[`stop`])},[t(i.ElTooltip,{content:i.$t(`@AGENT:重命名`),placement:`top`,"show-after":500},{default:u(()=>[y(`button`,{class:`item-action-btn`,onClick:t=>i.handleRename(e.sessionId,e.title,t)},[t(i.ElIcon,null,{default:u(()=>[t(i.Edit)]),_:1})],8,se)]),_:2},1032,[`content`]),t(i.ElTooltip,{content:i.$t(`@AGENT:删除`),placement:`top`,"show-after":500},{default:u(()=>[y(`button`,{class:`item-action-btn danger`,onClick:t=>i.handleDelete(e.sessionId,t)},[t(i.ElIcon,null,{default:u(()=>[t(i.Delete)]),_:1})],8,ce)]),_:2},1032,[`content`])])],10,te))),128))])),[[s,i.sessionsLoading]])],4),p(` 拖拽分隔条 `),y(`div`,{class:T([`sidebar-resizer`,{active:i.isResizing}]),onMousedown:i.startResize},null,34),p(` ═══ 右侧:对话区域 ═══ `),y(`main`,le,[p(` 加载中 `),i.sessionLoading?(M(),j(`div`,ue,[t(i.ElIcon,{class:`is-loading`,size:32},{default:u(()=>[t(i.Loading)]),_:1}),y(`span`,null,m(i.$t(`@AGENT:加载会话中...`)),1)])):(M(),j(A,{key:1},[p(` ChatContainer `),t(i.ChatContainer,{ref:`chatContainerRef`,messages:i.messages,"preset-questions":i.presetQuestions,"welcome-title":i.$t(`@AGENT:智能体助手`),"welcome-description":i.$t(`@AGENT:我可以帮你阅读代码、执行命令、修改文件。选择下方话题或直接输入你的问题。`),"assistant-name":`g ai`,theme:i.chatTheme,disabled:i.isStreaming,"upload-config":{accept:`image/*`},placeholder:i.isStreaming?i.$t(`@AGENT:正在生成中...`):i.$t(`@AGENT:输入消息,Enter 发送`),onSend:i.onSend,onSelect:i.onSelectPreset},null,8,[`messages`,`preset-questions`,`welcome-title`,`welcome-description`,`theme`,`disabled`,`placeholder`])],2112)),p(` 停止按钮浮层 `),t(k,{name:`fade`},{default:u(()=>[i.isStreaming?(M(),j(`div`,de,[y(`button`,{class:`stop-button`,onClick:n[2]||=(...e)=>i.stop&&i.stop(...e)},[t(i.ElIcon,null,{default:u(()=>[t(i.Loading,{class:`is-loading`})]),_:1}),y(`span`,null,m(i.$t(`@AGENT:停止生成`)),1)])])):p(`v-if`,!0)]),_:1})])])}var pe=I(W,[[`render`,fe],[`__scopeId`,`data-v-bd681ddd`],[`__file`,`C:/workspace/github_workspace/zen-gitsync/src/ui/client/src/views/AgentView.vue`]]);export{pe as default};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-BM3Ffeng.js";import{B as e,Cn as t,D as n,Et as r,Fn as i,G as a,Jn as o,Ln as s,Mn as c,Sn as l,Tn as u,Xn as d,Y as f,_n as p,_r as m,a as h,gn as g,hn as _,i as v,mn as y,mr as b,nt as x,rt as S,sr as C,vn as w,zn as T}from"./element-plus-C0loJL0J.js";import{E,T as D}from"./index-DLfjXhwQ.js";async function O(e,t){let n=e.body.getReader(),r=new TextDecoder(`utf-8`),i=``;for(;;){let{done:e,value:a}=await n.read();if(e)break;i+=r.decode(a,{stream:!0});let o=i.split(`
|
|
2
|
-
`);i=o.pop()??``;for(let e of o)e.trim()&&t(e)}i.trim()&&t(i)}async function k(){let e=await fetch(`/api/app-version`);return e.ok?e.json():{success:!1,current:``,latest:null,hasUpdate:!1,error:`HTTP ${e.status}`}}async function A(e){let t=await fetch(`/api/app-upgrade`,{method:`POST`});if(!t.ok){let n=`HTTP ${t.status}`;try{let e=await t.json();e?.error&&(n=e.error)}catch{}e({type:`error`,message:n}),e({type:`done`,code:t.status});return}if(!t.body){e({type:`error`,message:`响应无 body`}),e({type:`done`,code:-1});return}await O(t,t=>{try{e(JSON.parse(t))}catch{}})}async function j(){let e=await fetch(`/api/app-restart`,{method:`POST`});if(!e.ok){let t=`HTTP ${e.status}`;try{let n=await e.json();n?.error&&(t=n.error)}catch{}throw Error(t)}if(!e.body)throw Error(`响应无 body`);return new Promise((t,n)=>{let r=!1,i=e=>{r||(r=!0,e())};O(e,e=>{try{let r=JSON.parse(e);r.type===`ready`&&typeof r.port==`number`&&r.port>0?i(()=>t(r.port)):r.type===`timeout`?i(()=>n(Error(`restart.timeout`))):r.type===`error`&&i(()=>n(Error(r.message||`restart.failed`)))}catch{}}).then(()=>{i(()=>n(Error(`restart.stream-closed`)))}).catch(()=>{})})}var M=u({__name:`UpgradeDialog`,props:{modelValue:{type:Boolean,required:!0},logs:{type:String,required:!0},status:{type:String,required:!0},countdown:{type:Number,required:!1,default:0}},emits:[`update:modelValue`,`retry`,`restart`,`cancel`],setup(e,{expose:t,emit:n}){t();let i=e,a=n,s=C(null);o(()=>i.logs,async()=>{await c(),s.value&&(s.value.scrollTop=s.value.scrollHeight)});function l(){i.status!==`running`&&(i.status===`success`&&i.countdown>0&&a(`cancel`),a(`update:modelValue`,!1))}function u(){a(`retry`)}function d(){a(`restart`)}function f(){a(`cancel`),a(`update:modelValue`,!1)}let p={props:i,emit:a,logEl:s,close:l,onRetry:u,onRestart:d,onLater:f,get Loading(){return r},get CircleCheck(){return x},get CircleClose(){return S},get $t(){return E}};return Object.defineProperty(p,`__isScriptSetup`,{enumerable:!1,value:!0}),p}}),N={key:3},P={key:4},F={key:5},I={key:0,class:`upgrade-hint`},L={key:0,class:`upgrade-countdown`};function R(r,i,a,o,s,c){let u=f,h=e,v=n;return T(),g(v,{"model-value":a.modelValue,"onUpdate:modelValue":i[0]||=e=>o.emit(`update:modelValue`,e),title:o.$t(`@F13B4:升级`),width:`680px`,"close-on-click-modal":!1,"close-on-press-escape":!1,"show-close":a.status!==`running`,"append-to-body":``,onClose:o.close},{footer:d(()=>[a.status===`failed`?(T(),g(h,{key:0,type:`primary`,onClick:o.onRetry},{default:d(()=>[l(m(o.$t(`@F13B4:重试`)),1)]),_:1})):p(`v-if`,!0),a.status===`success`?(T(),g(h,{key:1,type:`primary`,onClick:o.onRestart},{default:d(()=>[l(m(o.$t(`@F13B4:立即重启并刷新`)),1)]),_:1})):p(`v-if`,!0),a.status===`success`?(T(),g(h,{key:2,onClick:o.onLater},{default:d(()=>[l(m(o.$t(`@F13B4:稍后手动重启`)),1)]),_:1})):p(`v-if`,!0),a.status===`running`?(T(),g(h,{key:3,disabled:``},{default:d(()=>[l(m(o.$t(`@F13B4:升级中`))+`... `,1)]),_:1})):p(`v-if`,!0)]),default:d(()=>[_(`div`,{class:b([`upgrade-status`,`is-${a.status}`])},[a.status===`running`?(T(),g(u,{key:0,class:`is-loading`},{default:d(()=>[t(o.Loading)]),_:1})):a.status===`success`?(T(),g(u,{key:1,color:`#67c23a`},{default:d(()=>[t(o.CircleCheck)]),_:1})):(T(),g(u,{key:2,color:`#f56c6c`},{default:d(()=>[t(o.CircleClose)]),_:1})),a.status===`running`?(T(),w(`span`,N,m(o.$t(`@F13B4:升级中`)),1)):a.status===`success`?(T(),w(`span`,P,m(o.$t(`@F13B4:升级完成`)),1)):(T(),w(`span`,F,m(o.$t(`@F13B4:升级失败`)),1))],2),a.status===`success`?(T(),w(`p`,I,[l(m(o.$t(`@F13B4:新版本已全局安装,需要重启服务才能生效`))+` `,1),a.countdown>0?(T(),w(`span`,L,m(o.$t(`@F13B4:{seconds} 秒后自动刷新`,{seconds:a.countdown})),1)):p(`v-if`,!0)])):p(`v-if`,!0),_(`pre`,{ref:`logEl`,class:`upgrade-log`},m(a.logs||o.$t(`@F13B4:等待日志输出`)),513)]),_:1},8,[`model-value`,`title`,`show-close`])}var z=D(M,[[`render`,R],[`__scopeId`,`data-v-bc566bba`],[`__file`,`C:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/UpgradeDialog.vue`]]),B=`https://github.com/xz333221/zen-gitsync/releases`,V=5,H=`app-version-check`,U=3600*1e3,W=u({__name:`AppVersionBadge`,setup(e,{expose:t}){t();let n=`2.16.13`,r=C(n),a=y(()=>r.value||n),o=C(null),c=C(!1),l=C(``),u=C(`running`),d=C(0),f=null,p=C(!1);function m(){try{let e=sessionStorage.getItem(H);if(!e)return null;let{at:t,data:n}=JSON.parse(e);return Date.now()-t>U?null:n}catch{return null}}function g(e){try{sessionStorage.setItem(H,JSON.stringify({at:Date.now(),data:e}))}catch{}}async function _(e=!1){let t=m();t&&(o.value=t);try{let t=await k();o.value=t,g(t),t.current&&(r.value=t.current),!e&&t.success&&t.hasUpdate&&h({type:`success`,message:E(`@F13B4:发现新版本 {version}`,{version:t.latest??``}),duration:4e3})}catch(t){e||h({type:`warning`,message:E(`@F13B4:检查更新失败 {error}`,{error:t?.message||``})})}}async function b(){if(o.value?.hasUpdate){try{await v.confirm(E(`@F13B4:确定要从 {current} 升级到 {latest} 吗?`,{current:o.value.current,latest:o.value.latest}),E(`@F13B4:升级`),{confirmButtonText:E(`@F13B4:确定升级`),cancelButtonText:E(`@F13B4:取消`),type:`info`})}catch{return}l.value=``,u.value=`running`,c.value=!0,await A(e=>{if(e.type===`done`){if(u.value=e.code===0?`success`:`failed`,e.code===0){try{sessionStorage.removeItem(H)}catch{}o.value?.latest&&(r.value=o.value.latest),x()}}else e.message&&(l.value+=e.message)})}}function x(){S(),d.value=V,f=setInterval(()=>{--d.value,d.value<=0&&(f&&=(clearInterval(f),null),T())},1e3)}function S(){f&&=(clearInterval(f),null),d.value=0}function w(){S()}async function T(){if(S(),!p.value){p.value=!0;try{let e=await j(),{pathname:t,search:n,hash:r}=window.location;window.location.href=`http://localhost:${e}${t}${n}${r}`}catch(e){let t=e?.message||``,n={"restart.timeout":`@F13B4:重启超时`,"restart.stream-closed":`@F13B4:重启失败,请刷新页面重试`,"restart.failed":`@F13B4:重启失败`}[t];h({type:`warning`,message:n?E(n):E(`@F13B4:重启失败 {error}`,{error:t})}),p.value=!1}}}i(()=>{S()}),s(()=>{_(!0)});let D={buildVersion:n,releasesUrl:B,runtimeVersion:r,displayVersion:a,latestInfo:o,upgradeDialogVisible:c,upgradeLogs:l,upgradeStatus:u,AUTO_REFRESH_SECONDS:V,countdown:d,get countdownTimer(){return f},set countdownTimer(e){f=e},restartInFlight:p,CACHE_KEY:H,CACHE_TTL_MS:U,readCache:m,writeCache:g,checkUpdate:_,handleUpgrade:b,startAutoRefreshCountdown:x,cancelAutoRefreshCountdown:S,handleCancelAutoRefresh:w,handleRestart:T,get $t(){return E},UpgradeDialog:z};return Object.defineProperty(D,`__isScriptSetup`,{enumerable:!1,value:!0}),D}}),G={class:`app-version-badge`},K=[`aria-label`],q=[`aria-label`],J=[`title`,`aria-label`],Y={class:`upgrade-btn__label`},X={class:`upgrade-btn__version`};function Z(e,n,r,i,o,s){let c=a;return T(),w(`div`,G,[p(` GitHub 链接 `),t(c,{content:i.$t(`@F13B4:在 GitHub 上查看源码`),placement:`top`,effect:`dark`,"show-after":300},{default:d(()=>[_(`a`,{href:`https://github.com/xz333221/zen-gitsync`,target:`_blank`,rel:`noopener noreferrer`,class:`badge-icon-link`,"aria-label":i.$t(`@F13B4:在 GitHub 上查看源码`)},[...n[1]||=[_(`svg`,{viewBox:`0 0 24 24`,width:`15`,height:`15`,"aria-hidden":`true`,fill:`currentColor`,xmlns:`http://www.w3.org/2000/svg`},[_(`path`,{d:`M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2z`})],-1)]],8,K)]),_:1},8,[`content`]),p(` NPM 链接 `),t(c,{content:i.$t(`@F13B4:在 npm 上查看包`),placement:`top`,effect:`dark`,"show-after":300},{default:d(()=>[_(`a`,{href:`https://www.npmjs.com/package/zen-gitsync`,target:`_blank`,rel:`noopener noreferrer`,class:`badge-icon-link badge-icon-link--npm`,"aria-label":i.$t(`@F13B4:在 npm 上查看包`)},[...n[2]||=[_(`svg`,{viewBox:`0 0 24 24`,width:`15`,height:`15`,"aria-hidden":`true`,fill:`currentColor`,xmlns:`http://www.w3.org/2000/svg`},[_(`path`,{d:`M2 2v20h20V2H2zm17.167 17.167H12.5v-10h-3.333v10H4.833V4.833h14.334v14.334z`})],-1)]],8,q)]),_:1},8,[`content`]),p(` 版本号 `),t(c,{content:i.$t(`@F13B4:查看更新`),placement:`top`,effect:`dark`,"show-after":300},{default:d(()=>[_(`a`,{href:i.releasesUrl,target:`_blank`,rel:`noopener noreferrer`,class:`version-link`},`v`+m(i.displayVersion),1)]),_:1},8,[`content`]),i.latestInfo?.hasUpdate?(T(),w(`button`,{key:0,type:`button`,class:`upgrade-btn`,title:i.$t(`@F13B4:发现新版本 {version},点击升级`,{version:i.latestInfo.latest??``}),"aria-label":i.$t(`@F13B4:发现新版本 {version},点击升级`,{version:i.latestInfo.latest??``}),onClick:i.handleUpgrade},[n[3]||=_(`span`,{class:`upgrade-btn__dot`,"aria-hidden":`true`},null,-1),_(`span`,Y,m(i.$t(`@F13B4:升级`)),1),_(`span`,X,`v`+m(i.latestInfo.latest),1)],8,J)):p(`v-if`,!0),t(i.UpgradeDialog,{modelValue:i.upgradeDialogVisible,"onUpdate:modelValue":n[0]||=e=>i.upgradeDialogVisible=e,logs:i.upgradeLogs,status:i.upgradeStatus,countdown:i.countdown,onRetry:i.handleUpgrade,onRestart:i.handleRestart,onCancel:i.handleCancelAutoRefresh},null,8,[`modelValue`,`logs`,`status`,`countdown`])])}var Q=D(W,[[`render`,Z],[`__scopeId`,`data-v-7e0300ec`],[`__file`,`C:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/AppVersionBadge.vue`]]);export{Q as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./rolldown-runtime-BM3Ffeng.js";import{Cn as e,K as t,L as n,Lt as r,Pt as i,R as a,Tn as o,Vn as s,W as c,Xn as l,Y as u,_n as d,_r as f,cn as p,gn as m,hn as h,kt as g,mn as _,mr as v,q as y,r as b,sr as x,tt as S,un as C,vn as w,zn as T}from"./element-plus-C0loJL0J.js";import{C as E,E as D,T as O,b as k,d as A,w as j}from"./index-DLfjXhwQ.js";var M=o({__name:`BranchSelector`,emits:[`branchChanged`],setup(e,{expose:t,emit:n}){t();let a=k(),o=n,s=x(!1),c=x(``),l=_(()=>{let e=c.value.trim().toLowerCase();return e?a.allBranches.filter(t=>t.toLowerCase().includes(e)):a.allBranches});async function u(e){if(e===a.currentBranch){s.value=!1;return}s.value=!1,await a.changeBranch(e)&&(o(`branchChanged`),a.refreshLog())}let d=x(!1),f=x(``),p=x(``);function m(){s.value=!1,p.value=a.currentBranch,d.value=!0}async function h(){await a.createBranch(f.value,p.value)&&(d.value=!1,f.value=``,o(`branchChanged`))}let v=x(!1);async function y(){if(!v.value){v.value=!0;try{await Promise.all([a.getCurrentBranch(!0),a.getBranchStatus(!0),a.getAllBranches()]),o(`branchChanged`),a.refreshLog()}finally{v.value=!1}}}let b={gitStore:a,emit:o,branchPopoverVisible:s,branchFilter:c,filteredBranches:l,selectBranch:u,createBranchDialogVisible:d,newBranchName:f,selectedBaseBranch:p,openCreateBranchDialog:m,createNewBranch:h,isRefreshingBranch:v,refreshCurrentBranch:y,get $t(){return D},get Plus(){return i},get Menu(){return g},get Check(){return S},get Refresh(){return r},CommonDialog:A,IconButton:E,SvgIcon:j};return Object.defineProperty(b,`__isScriptSetup`,{enumerable:!1,value:!0}),b}}),N={key:0,class:`branch-info`},P={class:`branch-wrapper`},F={class:`branch-btn`},I={class:`branch-btn-name`},L={class:`branch-btn-caret`},R={class:`branch-popover-inner`},z={class:`branch-search`},B={class:`branch-list`},V=[`onClick`],H={key:1,class:`branch-item-check-placeholder`},U={class:`branch-item-name`},W={key:0,class:`branch-empty`},G={class:`branch-popover-footer`},K={class:`create-branch-content`},q={class:`form-label`},J={class:`form-label`},Y={class:`dialog-footer`},X={class:`footer-actions`},Z=[`disabled`];function Q(r,i,o,g,_,x){let S=u,E=c,D=b,O=y,k=n,A=a,j=t;return g.gitStore.currentBranch?(T(),w(`div`,N,[h(`div`,P,[d(` VSCode 风格分支按钮 + 弹出列表 `),e(D,{visible:g.branchPopoverVisible,"onUpdate:visible":i[1]||=e=>g.branchPopoverVisible=e,placement:`top-start`,width:260,trigger:`click`,"popper-class":`branch-popover`,"show-arrow":!1,onShow:i[2]||=e=>g.branchFilter=``},{reference:l(()=>[h(`button`,F,[e(g.SvgIcon,{"icon-class":`git-branch`,"class-name":`branch-btn-icon`}),h(`span`,I,f(g.gitStore.currentBranch),1),h(`span`,L,[e(S,null,{default:l(()=>[...i[7]||=[h(`svg`,{viewBox:`0 0 1024 1024`,xmlns:`http://www.w3.org/2000/svg`},[h(`path`,{fill:`currentColor`,d:`M831.872 340.864 512 652.672 192.128 340.864a30.592 30.592 0 0 0-42.752 0 29.12 29.12 0 0 0 0 41.6L489.664 714.24a32 32 0 0 0 44.672 0l340.288-331.776a29.12 29.12 0 0 0 0-41.6 30.592 30.592 0 0 0-42.752 0z`})],-1)]]),_:1})])])]),default:l(()=>[h(`div`,R,[d(` 搜索框 `),h(`div`,z,[e(E,{modelValue:g.branchFilter,"onUpdate:modelValue":i[0]||=e=>g.branchFilter=e,placeholder:g.$t(`@F13B4:搜索或签出分支`),size:`small`,clearable:``,autofocus:``},null,8,[`modelValue`,`placeholder`])]),d(` 分支列表 `),h(`div`,B,[(T(!0),w(C,null,s(g.filteredBranches,t=>(T(),w(`div`,{key:t,class:v([`branch-item`,{"is-active":t===g.gitStore.currentBranch}]),onClick:e=>g.selectBranch(t)},[t===g.gitStore.currentBranch?(T(),m(S,{key:0,class:`branch-item-check`},{default:l(()=>[e(g.Check)]),_:1})):(T(),w(`span`,H)),e(g.SvgIcon,{"icon-class":`git-branch`,"class-name":`branch-item-icon`}),h(`span`,U,f(t),1)],10,V))),128)),g.filteredBranches.length===0?(T(),w(`div`,W,f(g.$t(`@F13B4:没有匹配的分支`)),1)):d(`v-if`,!0)]),d(` 底部:创建新分支 `),h(`div`,G,[h(`button`,{class:`branch-create-btn`,onClick:g.openCreateBranchDialog},[e(S,null,{default:l(()=>[e(g.Plus)]),_:1}),h(`span`,null,f(g.$t(`@F13B4:创建新分支`)),1)])])])]),_:1},8,[`visible`]),d(` 刷新按钮 `),e(g.IconButton,{tooltip:g.$t(`@CMDCON:刷新`),onClick:g.refreshCurrentBranch},{default:l(()=>[e(S,{class:v({"is-loading":g.isRefreshingBranch})},{default:l(()=>[e(g.Refresh)]),_:1},8,[`class`])]),_:1},8,[`tooltip`])]),d(` 创建分支对话框 `),e(g.CommonDialog,{modelValue:g.createBranchDialogVisible,"onUpdate:modelValue":i[6]||=e=>g.createBranchDialogVisible=e,title:g.$t(`@F13B4:创建新分支`),size:`small`,"destroy-on-close":!0,"append-to-body":!0,"custom-class":`create-branch-dialog`},{footer:l(()=>[h(`div`,Y,[h(`div`,X,[h(`button`,{type:`button`,class:`dialog-cancel-btn`,onClick:i[5]||=e=>g.createBranchDialogVisible=!1},f(g.$t(`@F13B4:取消`)),1),h(`button`,{type:`button`,class:`dialog-confirm-btn`,onClick:g.createNewBranch,disabled:g.gitStore.isCreatingBranch},[g.gitStore.isCreatingBranch?(T(),m(S,{key:1,class:`is-loading`},{default:l(()=>[...i[8]||=[h(`svg`,{viewBox:`0 0 1024 1024`,xmlns:`http://www.w3.org/2000/svg`},[h(`path`,{fill:`currentColor`,d:`M512 64a32 32 0 0 1 32 32v192a32 32 0 0 1-64 0V96a32 32 0 0 1 32-32zm0 640a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V736a32 32 0 0 1 32-32zm448-192a32 32 0 0 1-32 32H736a32 32 0 1 1 0-64h192a32 32 0 0 1 32 32zm-640 0a32 32 0 0 1-32 32H96a32 32 0 0 1 0-64h192a32 32 0 0 1 32 32z`})],-1)]]),_:1})):(T(),m(S,{key:0},{default:l(()=>[e(g.Check)]),_:1})),h(`span`,null,f(g.$t(`@F13B4:创建`)),1)],8,Z)])])]),default:l(()=>[h(`div`,K,[e(j,{model:{newBranchName:g.newBranchName,selectedBaseBranch:g.selectedBaseBranch},"label-position":`top`},{default:l(()=>[e(O,null,{label:l(()=>[h(`div`,q,[e(S,{class:`label-icon`},{default:l(()=>[e(g.Plus)]),_:1}),h(`span`,null,f(g.$t(`@F13B4:新分支名称`)),1)])]),default:l(()=>[e(E,{modelValue:g.newBranchName,"onUpdate:modelValue":i[3]||=e=>g.newBranchName=e,placeholder:g.$t(`@F13B4:请输入新分支名称`),class:`modern-input`,size:`large`,onKeyup:p(g.createNewBranch,[`enter`])},null,8,[`modelValue`,`placeholder`])]),_:1}),e(O,null,{label:l(()=>[h(`div`,J,[e(S,{class:`label-icon`},{default:l(()=>[e(g.Menu)]),_:1}),h(`span`,null,f(g.$t(`@F13B4:基于分支`)),1)])]),default:l(()=>[e(A,{modelValue:g.selectedBaseBranch,"onUpdate:modelValue":i[4]||=e=>g.selectedBaseBranch=e,placeholder:g.$t(`@F13B4:选择基础分支`),class:`modern-select`,size:`large`,style:{width:`100%`}},{default:l(()=>[(T(!0),w(C,null,s(g.gitStore.allBranches,e=>(T(),m(k,{key:e,label:e,value:e},null,8,[`label`,`value`]))),128))]),_:1},8,[`modelValue`,`placeholder`])]),_:1})]),_:1},8,[`model`])])]),_:1},8,[`modelValue`,`title`])])):d(`v-if`,!0)}var $=O(M,[[`render`,Q],[`__scopeId`,`data-v-9548a76f`],[`__file`,`C:/workspace/github_workspace/zen-gitsync/src/ui/client/src/components/BranchSelector.vue`]]);export{$ as default};
|