zen-gitsync 2.0.1 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-BcTk2R6G.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/vendor-BAXrrwNU.js">
8
+ <script type="module" crossorigin src="/assets/index-DBck3u67.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/vendor-CdJ34PvS.js">
10
10
  <link rel="stylesheet" crossorigin href="/assets/vendor-Dp0FkvMe.css">
11
- <link rel="stylesheet" crossorigin href="/assets/index-ChUZ1vPG.css">
11
+ <link rel="stylesheet" crossorigin href="/assets/index-D5irnfho.css">
12
12
  </head>
13
13
  <body>
14
14
  <div id="app"></div>
@@ -5,6 +5,9 @@ import path from 'path';
5
5
  import { execGitCommand } from '../../utils/index.js';
6
6
  import open from 'open';
7
7
  import config from '../../config.js';
8
+ import chalk from 'chalk';
9
+ import fs from 'fs/promises';
10
+ import os from 'os';
8
11
  // import { Server } from 'socket.io';
9
12
  // import { exec } from 'child_process';
10
13
 
@@ -20,6 +23,13 @@ async function startUIServer() {
20
23
  // 添加全局中间件来解析JSON请求体
21
24
  app.use(express.json());
22
25
 
26
+ // 添加请求日志中间件
27
+ app.use((req, res, next) => {
28
+ const now = new Date().toLocaleString();
29
+ console.log(`[${now}] ${req.method} ${req.url}`);
30
+ next();
31
+ });
32
+
23
33
  // // 启动前端Vue应用
24
34
  // const clientPath = path.join(__dirname, '../client');
25
35
  // console.log(`正在启动前端应用,路径: ${clientPath}`);
@@ -185,6 +195,103 @@ async function startUIServer() {
185
195
  }
186
196
  });
187
197
 
198
+ // 新增切换工作目录接口
199
+ app.post('/api/change_directory', async (req, res) => {
200
+ try {
201
+
202
+ const { path } = req.body;
203
+
204
+ if (!path) {
205
+ return res.status(400).json({ success: false, error: '目录路径不能为空' });
206
+ }
207
+
208
+ try {
209
+ process.chdir(path);
210
+ const newDirectory = process.cwd();
211
+
212
+ // 检查新目录是否是Git仓库
213
+ try {
214
+ await execGitCommand('git rev-parse --is-inside-work-tree');
215
+ res.json({
216
+ success: true,
217
+ directory: newDirectory,
218
+ isGitRepo: true
219
+ });
220
+ } catch (error) {
221
+ res.json({
222
+ success: true,
223
+ directory: newDirectory,
224
+ isGitRepo: false,
225
+ warning: '新目录不是一个Git仓库'
226
+ });
227
+ }
228
+ } catch (error) {
229
+ res.status(400).json({
230
+ success: false,
231
+ error: `切换到目录 "${path}" 失败: ${error.message}`
232
+ });
233
+ }
234
+ } catch (error) {
235
+ res.status(500).json({ error: error.message });
236
+ }
237
+ });
238
+
239
+ // 获取目录内容(用于浏览目录)
240
+ app.get('/api/browse_directory', async (req, res) => {
241
+ try {
242
+
243
+ // 获取要浏览的目录路径,如果没有提供,则使用当前目录
244
+ const directoryPath = req.query.path || process.cwd();
245
+
246
+ try {
247
+ // 读取目录内容
248
+ const items = await fs.readdir(directoryPath, { withFileTypes: true });
249
+
250
+ // 分离文件夹和文件
251
+ const directories = [];
252
+ const files = [];
253
+
254
+ for (const item of items) {
255
+ const fullPath = path.join(directoryPath, item.name);
256
+ if (item.isDirectory()) {
257
+ directories.push({
258
+ name: item.name,
259
+ path: fullPath,
260
+ type: 'directory'
261
+ });
262
+ } else if (item.isFile()) {
263
+ files.push({
264
+ name: item.name,
265
+ path: fullPath,
266
+ type: 'file'
267
+ });
268
+ }
269
+ }
270
+
271
+ // 优先显示目录,然后是文件,都按字母排序
272
+ directories.sort((a, b) => a.name.localeCompare(b.name));
273
+ files.sort((a, b) => a.name.localeCompare(b.name));
274
+
275
+ // 获取父目录路径
276
+ const parentPath = path.dirname(directoryPath);
277
+
278
+ res.json({
279
+ success: true,
280
+ currentPath: directoryPath,
281
+ parentPath: parentPath !== directoryPath ? parentPath : null,
282
+ items: [...directories, ...files]
283
+ });
284
+ } catch (error) {
285
+ res.status(400).json({
286
+ success: false,
287
+ error: `无法读取目录 "${directoryPath}": ${error.message}`
288
+ });
289
+ }
290
+ } catch (error) {
291
+ res.status(500).json({ error: error.message });
292
+ }
293
+ });
294
+
188
295
  // 获取配置
189
296
  app.get('/api/config/getConfig', async (req, res) => {
190
297
  try {
@@ -277,6 +384,53 @@ async function startUIServer() {
277
384
  }
278
385
  })
279
386
 
387
+ // 更新模板
388
+ app.post('/api/config/update-template', express.json(), async (req, res) => {
389
+ try {
390
+ const { oldTemplate, newTemplate, type } = req.body
391
+
392
+ if (!oldTemplate || !newTemplate || !type) {
393
+ return res.status(400).json({ success: false, error: '缺少必要参数' })
394
+ }
395
+
396
+ const config = await configManager.loadConfig()
397
+
398
+ if (type === 'description') {
399
+ // 确保描述模板数组存在
400
+ if (config.descriptionTemplates) {
401
+ const index = config.descriptionTemplates.indexOf(oldTemplate)
402
+ if (index !== -1) {
403
+ config.descriptionTemplates[index] = newTemplate
404
+ await configManager.saveConfig(config)
405
+ } else {
406
+ return res.status(404).json({ success: false, error: '未找到原模板' })
407
+ }
408
+ } else {
409
+ return res.status(404).json({ success: false, error: '模板列表不存在' })
410
+ }
411
+ } else if (type === 'scope') {
412
+ // 确保作用域模板数组存在
413
+ if (config.scopeTemplates) {
414
+ const index = config.scopeTemplates.indexOf(oldTemplate)
415
+ if (index !== -1) {
416
+ config.scopeTemplates[index] = newTemplate
417
+ await configManager.saveConfig(config)
418
+ } else {
419
+ return res.status(404).json({ success: false, error: '未找到原模板' })
420
+ }
421
+ } else {
422
+ return res.status(404).json({ success: false, error: '模板列表不存在' })
423
+ }
424
+ } else {
425
+ return res.status(400).json({ success: false, error: '不支持的模板类型' })
426
+ }
427
+
428
+ res.json({ success: true })
429
+ } catch (error) {
430
+ res.status(500).json({ success: false, error: error.message })
431
+ }
432
+ })
433
+
280
434
  // 提交更改
281
435
  app.post('/api/commit', express.json(), async (req, res) => {
282
436
  try {
@@ -341,8 +495,11 @@ async function startUIServer() {
341
495
  // 获取日志
342
496
  app.get('/api/log', async (req, res) => {
343
497
  try {
344
- // 获取请求参数中的数量限制,默认为100
345
- const limit = req.query.all === 'true' ? '' : '-n 100';
498
+ // 获取请求参数中的数量限制,默认为30
499
+ const limit = req.query.all === 'true' ? '' : '-n 30';
500
+
501
+ // graph参数保留但不做特殊处理,避免前端代码重复调用API
502
+ // 由前端统一使用该接口
346
503
 
347
504
  // 修改 git log 命令,添加 %ae 参数来获取作者邮箱
348
505
  const { stdout } = await execGitCommand(`git log --all --pretty=format:"%h|%an|%ae|%ad|%s|%D" --date=short ${limit}`);
@@ -382,6 +539,97 @@ async function startUIServer() {
382
539
  }
383
540
  });
384
541
 
542
+ // 撤回文件修改
543
+ app.post('/api/revert_file', async (req, res) => {
544
+ try {
545
+ const { filePath } = req.body;
546
+
547
+ if (!filePath) {
548
+ return res.status(400).json({
549
+ success: false,
550
+ error: '缺少文件路径参数'
551
+ });
552
+ }
553
+
554
+ // 检查文件状态:未跟踪文件需要删除,修改文件需要恢复
555
+ const { stdout: statusOutput } = await execGitCommand(`git status --porcelain -- "${filePath}"`);
556
+
557
+ // 未跟踪的文件 (??), 需要删除它
558
+ if (statusOutput.startsWith('??')) {
559
+ try {
560
+ await fs.unlink(filePath);
561
+ return res.json({ success: true, message: '未跟踪的文件已删除' });
562
+ } catch (error) {
563
+ return res.status(500).json({
564
+ success: false,
565
+ error: `删除文件失败: ${error.message}`
566
+ });
567
+ }
568
+ }
569
+ // 已暂存的文件,先取消暂存
570
+ else if (statusOutput.startsWith('A ') || statusOutput.startsWith('M ') || statusOutput.startsWith('D ')) {
571
+ // 先取消暂存
572
+ await execGitCommand(`git reset HEAD -- "${filePath}"`);
573
+ }
574
+
575
+ // 已修改文件,取消所有本地修改
576
+ if (statusOutput) {
577
+ await execGitCommand(`git checkout -- "${filePath}"`);
578
+ return res.json({ success: true, message: '文件修改已撤回' });
579
+ } else {
580
+ return res.status(400).json({
581
+ success: false,
582
+ error: '文件没有修改或不存在'
583
+ });
584
+ }
585
+ } catch (error) {
586
+ console.error('撤回文件修改失败:', error);
587
+ res.status(500).json({
588
+ success: false,
589
+ error: `撤回文件修改失败: ${error.message}`
590
+ });
591
+ }
592
+ });
593
+
594
+ // 重置暂存区 (git reset HEAD)
595
+ app.post('/api/reset-head', async (req, res) => {
596
+ try {
597
+ // 执行 git reset HEAD 命令
598
+ await execGitCommand('git reset HEAD');
599
+ res.json({ success: true });
600
+ } catch (error) {
601
+ console.error('重置暂存区失败:', error);
602
+ res.status(500).json({
603
+ success: false,
604
+ error: `重置暂存区失败: ${error.message}`
605
+ });
606
+ }
607
+ });
608
+
609
+ // 重置到远程分支 (git reset --hard origin/branch)
610
+ app.post('/api/reset-to-remote', async (req, res) => {
611
+ try {
612
+ const { branch } = req.body;
613
+
614
+ if (!branch) {
615
+ return res.status(400).json({
616
+ success: false,
617
+ error: '缺少分支名称参数'
618
+ });
619
+ }
620
+
621
+ // 执行 git reset --hard origin/branch 命令
622
+ await execGitCommand(`git reset --hard origin/${branch}`);
623
+ res.json({ success: true });
624
+ } catch (error) {
625
+ console.error('重置到远程分支失败:', error);
626
+ res.status(500).json({
627
+ success: false,
628
+ error: `重置到远程分支失败: ${error.message}`
629
+ });
630
+ }
631
+ });
632
+
385
633
  // Socket.io 实时更新
386
634
  // io.on('connection', (socket) => {
387
635
  // console.log('客户端已连接');
@@ -405,7 +653,11 @@ async function startUIServer() {
405
653
  // 启动服务器
406
654
  const PORT = 3000;
407
655
  httpServer.listen(PORT, () => {
408
- console.log(`后端API服务器已启动: http://localhost:${PORT}`);
656
+ console.log(chalk.green('======================================'));
657
+ console.log(chalk.green(` Zen GitSync 服务器已启动`));
658
+ console.log(chalk.green(` 访问地址: http://localhost:${PORT}`));
659
+ console.log(chalk.green(` 启动时间: ${new Date().toLocaleString()}`));
660
+ console.log(chalk.green('======================================'));
409
661
  open(`http://localhost:${PORT}`);
410
662
  }).on('error', async (err) => {
411
663
  if (err.code === 'EADDRINUSE') {
@@ -415,7 +667,11 @@ async function startUIServer() {
415
667
  try {
416
668
  await new Promise((resolve, reject) => {
417
669
  httpServer.listen(newPort, () => {
418
- console.log(`后端API服务器已启动: http://localhost:${newPort}`);
670
+ console.log(chalk.green('======================================'));
671
+ console.log(chalk.green(` Zen GitSync 服务器已启动`));
672
+ console.log(chalk.green(` 访问地址: http://localhost:${newPort}`));
673
+ console.log(chalk.green(` 启动时间: ${new Date().toLocaleString()}`));
674
+ console.log(chalk.green('======================================'));
419
675
  open(`http://localhost:${newPort}`);
420
676
  resolve();
421
677
  }).on('error', (e) => {
@@ -1,9 +0,0 @@
1
- import{d as ee,r as o,o as te,c as y,a as m,b as l,e as H,f as s,E as he,w as u,u as x,g as Be,t as _,h as K,i as Oe,F as J,j as q,k as re,l as ye,v as _e,m as Le,n as Ne,p as r,q as Ae,s as fe,x as De,y as Ee,z as Me,A as we,B as D,C as be,D as ke,G as ve,H as G,I as Pe,J as Re,K as ze,L as Ie,M as Q,N as Ue,O as Fe,P as Ge,Q as He,R as Je,S as We,T as qe}from"./vendor-BAXrrwNU.js";(function(){const E=document.createElement("link").relList;if(E&&E.supports&&E.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))v(a);new MutationObserver(a=>{for(const g of a)if(g.type==="childList")for(const T of g.addedNodes)T.tagName==="LINK"&&T.rel==="modulepreload"&&v(T)}).observe(document,{childList:!0,subtree:!0});function d(a){const g={};return a.integrity&&(g.integrity=a.integrity),a.referrerPolicy&&(g.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?g.credentials="include":a.crossOrigin==="anonymous"?g.credentials="omit":g.credentials="same-origin",g}function v(a){if(a.ep)return;a.ep=!0;const g=d(a);fetch(a.href,g)}})();const Ke={class:"card"},Ze={class:"current-directory"},Qe={class:"status-header"},Xe={class:"status-box"},Ye={key:0,class:"file-list"},et=["onClick"],tt={class:"file-type"},st={class:"file-path"},at={class:"diff-content"},lt=["innerHTML"],ot={key:1,class:"no-diff"},nt={class:"file-navigation"},it={class:"file-counter"};function rt(w){return w==="added"?"新增":w==="modified"?"修改":w==="deleted"?"删除":w==="untracked"?"未跟踪":"其它"}const ct=ee({__name:"GitStatus",setup(w,{expose:E}){const d=o("加载中..."),v=o(!1),a=o([]),g=o(""),T=o(""),j=o(!1),A=o(!1),B=o(-1);function R(c){if(c===void 0)return;const f=c.split(`
2
- `),b=[];for(const n of f){const t=n.match(/^([ MADRCU\?]{2})\s+(.+)$/);if(t){let V="";const C=t[1].trim();C==="M"||C==="MM"||C==="AM"||C==="RM"?V="modified":C==="A"||C==="AA"?V="added":C==="D"||C==="AD"||C==="DA"?V="deleted":C==="??"?V="untracked":V="other",b.push({path:t[2],type:V})}}a.value=b}const z=o("");async function L(){try{v.value=!0;const f=await(await fetch("/api/current_directory")).json();if(z.value=f.directory||"未知目录",f.isGitRepo===!1){d.value="当前目录不是一个Git仓库",a.value=[],r.warning("当前目录不是一个Git仓库");return}const n=await(await fetch("/api/status")).json();d.value=n.status;const V=await(await fetch("/api/status_porcelain")).json();R(V.status),r({message:"Git 状态已刷新",type:"success"})}catch(c){d.value="加载状态失败: "+c.message,a.value=[],r({message:"刷新失败: "+c.message,type:"error"})}finally{v.value=!1}}function I(c){if(!c)return"";const f=c.split(`
3
- `);function b(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}return f.map(n=>{const t=b(n);return n.startsWith("diff --git")?`<div class="diff-header">${t}</div>`:n.startsWith("---")?`<div class="diff-old-file">${t}</div>`:n.startsWith("+++")?`<div class="diff-new-file">${t}</div>`:n.startsWith("@@")?`<div class="diff-hunk-header">${t}</div>`:n.startsWith("+")?`<div class="diff-added">${t}</div>`:n.startsWith("-")?`<div class="diff-removed">${t}</div>`:`<div class="diff-context">${t}</div>`}).join("")}async function N(c){try{A.value=!0,g.value=c,B.value=a.value.findIndex(n=>n.path===c);const b=await(await fetch(`/api/diff?file=${encodeURIComponent(c)}`)).json();T.value=b.diff||"没有变更",j.value=!0}catch(f){r({message:"获取文件差异失败: "+f.message,type:"error"}),T.value="获取差异失败: "+f.message}finally{A.value=!1}}async function k(){if(a.value.length===0||B.value<=0)return;const c=B.value-1,f=a.value[c];await N(f.path)}async function S(){if(a.value.length===0||B.value>=a.value.length-1)return;const c=B.value+1,f=a.value[c];await N(f.path)}function h(c){N(c.path)}async function $(){await L()}return te(()=>{L()}),E({refreshStatus:$}),(c,f)=>{const b=he,n=K,t=re,V=_e;return m(),y("div",Ke,[l("div",Ze,[s(b,null,{default:u(()=>[s(x(Be))]),_:1}),l("span",null,_(z.value),1)]),l("div",Qe,[f[1]||(f[1]=l("h2",null,"Git 状态",-1)),s(n,{type:"primary",icon:x(Oe),circle:"",size:"small",onClick:$,loading:v.value},null,8,["icon","loading"])]),l("div",Xe,_(d.value),1),a.value.length?(m(),y("div",Ye,[(m(!0),y(J,null,q(a.value,C=>(m(),y("div",{key:C.path,class:Ae(["file-item",C.type]),onClick:W=>h(C)},[l("span",tt,_(rt(C.type)),1),l("span",st,_(C.path),1)],10,et))),128))])):H("",!0),s(t,{modelValue:j.value,"onUpdate:modelValue":f[0]||(f[0]=C=>j.value=C),title:`文件差异: ${g.value}`,width:"80%","destroy-on-close":""},{default:u(()=>[ye((m(),y("div",at,[T.value?(m(),y("div",{key:0,innerHTML:I(T.value),class:"diff-formatted"},null,8,lt)):(m(),y("div",ot,"该文件没有差异或是新文件"))])),[[V,A.value]]),l("div",nt,[s(n,{icon:x(Le),onClick:k,disabled:B.value<=0||a.value.length===0,circle:""},null,8,["icon","disabled"]),l("span",it,_(B.value+1)+" / "+_(a.value.length),1),s(n,{icon:x(Ne),onClick:S,disabled:B.value>=a.value.length-1||a.value.length===0,circle:""},null,8,["icon","disabled"])])]),_:1},8,["modelValue","title"])])}}}),se=(w,E)=>{const d=w.__vccOpts||w;for(const[v,a]of E)d[v]=a;return d},ut=se(ct,[["__scopeId","data-v-849f0626"]]),dt={class:"card"},pt={class:"commit-options"},mt={class:"commit-mode-toggle"},ft={class:"no-verify-toggle"},vt={key:0,class:"commit-form"},gt={key:1,class:"standard-commit-form"},ht={class:"standard-commit-header"},yt={class:"scope-container"},_t={class:"description-container"},wt={class:"preview-section"},bt={class:"preview-content"},kt={class:"button-group"},Ct={class:"template-container"},$t={class:"template-form"},Vt={class:"template-list"},St={class:"template-content"},Tt={class:"template-actions"},xt={class:"template-container"},jt={class:"template-form"},Bt={class:"template-list"},Ot={class:"template-content"},Lt={class:"template-actions"},Nt=ee({__name:"CommitForm",emits:["commit-success","push-success"],setup(w,{emit:E}){const d=E,v=o(""),a=o("提交"),g=o("推送到远程"),T=o(!1),j=o(!1),A=o(!1),B=o("提交并推送"),R=o("输入提交信息..."),z=o(""),L=o(!1),I=o("feat"),N=o(""),k=o(""),S=o(""),h=o(""),$=o([]),c=o(!1),f=o(""),b=o([]),n=o(!1),t=o(""),V=o(!1),C=[{value:"feat",label:"feat: 新功能"},{value:"fix",label:"fix: 修复bug"},{value:"docs",label:"docs: 文档修改"},{value:"style",label:"style: 样式修改"},{value:"refactor",label:"refactor: 代码重构"},{value:"test",label:"test: 测试代码"},{value:"chore",label:"chore: 构建/工具修改"}];fe(L,i=>{localStorage.setItem("zen-gitsync-standard-commit",i.toString())}),fe(V,i=>{localStorage.setItem("zen-gitsync-skip-hooks",i.toString())});const W=De(()=>{if(!L.value)return v.value||z.value;let i=`${I.value||""}`;return N.value&&(i+=`(${N.value})`),i+=`: ${k.value}`,S.value&&(i+=`
4
-
5
- ${S.value}`),h.value&&(i+=`
6
-
7
- ${h.value}`),i});async function X(){try{const e=await(await fetch("/api/config/getConfig")).json();R.value=`输入提交信息 (默认: ${e.defaultCommitMessage})`,z.value=e.defaultCommitMessage||"",e.descriptionTemplates&&Array.isArray(e.descriptionTemplates)&&($.value=e.descriptionTemplates),e.scopeTemplates&&Array.isArray(e.scopeTemplates)&&(b.value=e.scopeTemplates)}catch(i){console.error("加载配置失败:",i)}}async function ae(){if(!f.value.trim()){r({message:"请输入模板内容",type:"warning"});return}try{if($.value.includes(f.value)){r({message:"该模板已存在",type:"warning"});return}$.value.push(f.value);const e=await(await fetch("/api/config/save-template",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template:f.value,type:"description"})})).json();e.success?(r({message:"模板保存成功!",type:"success"}),f.value=""):r({message:"模板保存失败: "+e.error,type:"error"})}catch(i){r({message:"模板保存失败: "+i.message,type:"error"})}}async function Z(){if(!t.value.trim()){r({message:"请输入模板内容",type:"warning"});return}try{if(b.value.includes(t.value)){r({message:"该模板已存在",type:"warning"});return}b.value.push(t.value);const e=await(await fetch("/api/config/save-template",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template:t.value,type:"scope"})})).json();e.success?(r({message:"作用域模板保存成功!",type:"success"}),t.value=""):r({message:"作用域模板保存失败: "+e.error,type:"error"})}catch(i){r({message:"作用域模板保存失败: "+i.message,type:"error"})}}async function le(i){try{const e=$.value.indexOf(i);e!==-1&&$.value.splice(e,1);const F=await(await fetch("/api/config/delete-template",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template:i,type:"description"})})).json();F.success?r({message:"模板删除成功!",type:"success"}):r({message:"模板删除失败: "+F.error,type:"error"})}catch(e){r({message:"模板删除失败: "+e.message,type:"error"})}}async function oe(i){try{const e=b.value.indexOf(i);e!==-1&&b.value.splice(e,1);const F=await(await fetch("/api/config/delete-template",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template:i,type:"scope"})})).json();F.success?r({message:"作用域模板删除成功!",type:"success"}):r({message:"作用域模板删除失败: "+F.error,type:"error"})}catch(e){r({message:"作用域模板删除失败: "+e.message,type:"error"})}}function O(i){k.value=i,c.value=!1}function Ce(i){N.value=i,n.value=!1}function $e(){c.value=!0}function Ve(){n.value=!0}function Se(){const i=localStorage.getItem("zen-gitsync-standard-commit");i!==null&&(L.value=i==="true");const e=localStorage.getItem("zen-gitsync-skip-hooks");e!==null&&(V.value=e==="true")}async function ce(){const i=W.value;if(!i&&L.value&&!k.value){r({message:"请输入提交描述",type:"warning"});return}try{T.value=!0,a.value="提交中...";const U=await(await fetch("/api/add",{method:"POST"})).json();if(!U.success){r({message:"添加文件失败: "+U.error,type:"error"});return}const M=await(await fetch("/api/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:i,hasNewlines:i.includes(`
8
- `),noVerify:V.value})})).json();M.success?(L.value?(k.value="",S.value="",h.value=""):v.value="",r({message:"提交成功!",type:"success"}),d("commit-success")):r({message:"提交失败: "+M.error,type:"error"})}catch(e){r({message:"提交失败: "+e.message,type:"error"})}finally{T.value=!1,a.value="提交"}}async function Te(){try{j.value=!0,g.value="推送中...";const e=await(await fetch("/api/push",{method:"POST"})).json();e.success?(r({message:"推送成功!",type:"success"}),d("push-success")):r({message:"推送失败: "+e.error,type:"error"})}catch(i){r({message:"推送失败: "+i.message,type:"error"})}finally{j.value=!1,g.value="推送到远程"}}async function xe(){const i=W.value;if(!i&&L.value&&!k.value){r({message:"请输入提交描述",type:"warning"});return}try{A.value=!0,B.value="处理中...";const U=await(await fetch("/api/add",{method:"POST"})).json();if(!U.success){r({message:"添加文件失败: "+U.error,type:"error"});return}const M=await(await fetch("/api/commit",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:i,hasNewlines:i.includes(`
9
- `),noVerify:V.value})})).json();if(!M.success){r({message:"提交失败: "+M.error,type:"error"});return}L.value?(k.value="",S.value="",h.value=""):v.value="";const Y=await(await fetch("/api/push",{method:"POST"})).json();Y.success?(v.value="",r({message:"提交并推送成功!",type:"success"}),d("commit-success"),d("push-success")):r({message:"推送失败: "+Y.error,type:"error"})}catch(e){r({message:"操作失败: "+e.message,type:"error"})}finally{A.value=!1,B.value="提交并推送"}}return te(()=>{X(),Se()}),(i,e)=>{const U=Ee,F=Me,M=we,P=K,Y=ke,je=be,ue=Pe,de=ze,pe=Re,me=re;return m(),y("div",dt,[e[21]||(e[21]=l("h2",null,"提交更改",-1)),l("div",pt,[l("div",mt,[s(U,{modelValue:L.value,"onUpdate:modelValue":e[0]||(e[0]=p=>L.value=p),"active-text":"标准化提交","inactive-text":"普通提交"},null,8,["modelValue"])]),l("div",ft,[s(F,{content:"跳过 Git 钩子检查 (--no-verify)",placement:"top"},{default:u(()=>[s(U,{modelValue:V.value,"onUpdate:modelValue":e[1]||(e[1]=p=>V.value=p),"active-text":"跳过钩子 (--no-verify)"},null,8,["modelValue"])]),_:1})])]),L.value?(m(),y("div",gt,[l("div",ht,[s(je,{modelValue:I.value,"onUpdate:modelValue":e[3]||(e[3]=p=>I.value=p),placeholder:"提交类型",class:"type-select",clearable:""},{default:u(()=>[(m(),y(J,null,q(C,p=>s(Y,{key:p.value,label:p.label,value:p.value},null,8,["label","value"])),64))]),_:1},8,["modelValue"]),l("div",yt,[s(M,{modelValue:N.value,"onUpdate:modelValue":e[4]||(e[4]=p=>N.value=p),placeholder:"作用域(可选)",class:"scope-input",clearable:""},null,8,["modelValue"]),s(P,{type:"primary",icon:x(ve),circle:"",size:"small",class:"settings-button",onClick:Ve},null,8,["icon"])]),l("div",_t,[s(M,{modelValue:k.value,"onUpdate:modelValue":e[5]||(e[5]=p=>k.value=p),placeholder:"简短描述(必填)",class:"description-input",clearable:""},null,8,["modelValue"]),s(P,{type:"primary",icon:x(ve),circle:"",size:"small",class:"settings-button",onClick:$e},null,8,["icon"])])]),s(M,{modelValue:S.value,"onUpdate:modelValue":e[6]||(e[6]=p=>S.value=p),type:"textarea",rows:4,placeholder:"正文(可选):详细描述本次提交的内容和原因",class:"body-input",clearable:""},null,8,["modelValue"]),s(M,{modelValue:h.value,"onUpdate:modelValue":e[7]||(e[7]=p=>h.value=p),placeholder:"页脚(可选):如 Closes #123",class:"footer-input",clearable:""},null,8,["modelValue"]),l("div",wt,[e[12]||(e[12]=l("div",{class:"preview-title"},"预览:",-1)),l("pre",bt,_(W.value),1)]),s(P,{type:"primary",onClick:ce,loading:T.value},{default:u(()=>[D(_(a.value),1)]),_:1},8,["loading"])])):(m(),y("div",vt,[s(M,{modelValue:v.value,"onUpdate:modelValue":e[2]||(e[2]=p=>v.value=p),placeholder:R.value,clearable:""},null,8,["modelValue","placeholder"]),s(P,{type:"primary",onClick:ce,loading:T.value},{default:u(()=>[D(_(a.value),1)]),_:1},8,["loading"])])),l("div",kt,[s(P,{type:"success",onClick:Te,loading:j.value},{default:u(()=>[D(_(g.value),1)]),_:1},8,["loading"]),s(P,{type:"warning",onClick:xe,loading:A.value},{default:u(()=>[D(_(B.value),1)]),_:1},8,["loading"])]),s(me,{title:"简短描述模板设置",modelValue:c.value,"onUpdate:modelValue":e[9]||(e[9]=p=>c.value=p),width:"80vw",style:{height:"80vh"}},{default:u(()=>[l("div",Ct,[l("div",$t,[s(M,{modelValue:f.value,"onUpdate:modelValue":e[8]||(e[8]=p=>f.value=p),placeholder:"输入新模板内容",class:"template-input",clearable:""},null,8,["modelValue"]),s(P,{type:"primary",onClick:ae,disabled:!f.value.trim()},{default:u(()=>e[13]||(e[13]=[D("添加模板")])),_:1},8,["disabled"])]),l("div",Vt,[e[16]||(e[16]=l("h3",null,"已保存模板",-1)),$.value.length===0?(m(),G(ue,{key:0,description:"暂无保存的模板"})):H("",!0),(m(!0),y(J,null,q($.value,(p,ne)=>(m(),G(pe,{key:ne,class:"template-item"},{default:u(()=>[s(de,{justify:"space-between",align:"middle",style:{width:"100%"}},{default:u(()=>[l("div",St,_(p),1),l("div",Tt,[s(P,{type:"primary",size:"small",onClick:ie=>O(p)},{default:u(()=>e[14]||(e[14]=[D("使用")])),_:2},1032,["onClick"]),s(P,{type:"danger",size:"small",onClick:ie=>le(p)},{default:u(()=>e[15]||(e[15]=[D("删除")])),_:2},1032,["onClick"])])]),_:2},1024)]),_:2},1024))),128))])])]),_:1},8,["modelValue"]),s(me,{title:"作用域模板设置",modelValue:n.value,"onUpdate:modelValue":e[11]||(e[11]=p=>n.value=p),width:"80%",style:{height:"80vh"}},{default:u(()=>[l("div",xt,[l("div",jt,[s(M,{modelValue:t.value,"onUpdate:modelValue":e[10]||(e[10]=p=>t.value=p),placeholder:"输入新作用域模板",class:"template-input",clearable:""},null,8,["modelValue"]),s(P,{type:"primary",onClick:Z,disabled:!t.value.trim()},{default:u(()=>e[17]||(e[17]=[D("添加模板")])),_:1},8,["disabled"])]),l("div",Bt,[e[20]||(e[20]=l("h3",null,"已保存作用域",-1)),b.value.length===0?(m(),G(ue,{key:0,description:"暂无保存的作用域"})):H("",!0),(m(!0),y(J,null,q(b.value,(p,ne)=>(m(),G(pe,{key:ne,class:"template-item"},{default:u(()=>[s(de,{justify:"space-between",align:"middle",style:{width:"100%"}},{default:u(()=>[l("div",Ot,_(p),1),l("div",Lt,[s(P,{type:"primary",size:"small",onClick:ie=>Ce(p)},{default:u(()=>e[18]||(e[18]=[D("使用")])),_:2},1032,["onClick"]),s(P,{type:"danger",size:"small",onClick:ie=>oe(p)},{default:u(()=>e[19]||(e[19]=[D("删除")])),_:2},1032,["onClick"])])]),_:2},1024)]),_:2},1024))),128))])])]),_:1},8,["modelValue"])])}}}),At=se(Nt,[["__scopeId","data-v-00e49634"]]),Dt={class:"card"},Et={class:"log-header"},Mt={class:"log-actions"},Pt={key:0},Rt={key:1},zt={key:0,class:"graph-view"},It={key:0,class:"commit-count"},Ut={key:1},Ft={key:0,class:"commit-count"},Gt={key:0,class:"branch-container"};function ge(w){return w=w.trim().replace(/^HEAD\s*->\s*/,""),w=w.replace(/^origin\//,""),w=w.replace(/^tag:\s*/,""),w.trim()}function Ht(w){return w.includes("HEAD")?"success":w.includes("tag:")?"warning":w.includes("origin/")?"info":"primary"}const Jt=ee({__name:"LogList",setup(w,{expose:E}){const d=o([]),v=o(""),a=o(!1),g=o(!1),T=o(0),j=o(!0),A=o(null);async function B(N=!1){try{a.value=!0,g.value=N;const S=await fetch(N?"/api/log?all=true&graph=true":"/api/log?graph=true");d.value=await S.json(),T.value=d.value.length,v.value="",j.value&&setTimeout(R,0)}catch(k){v.value="加载提交历史失败: "+k.message}finally{a.value=!1}}async function R(){if(!A.value||d.value.length===0)return;A.value.innerHTML="";const N=await fetch("/api/branch"),{branch:k}=await N.json(),S=Ge(A.value,{orientation:"vertical-reverse",template:"metro",author:"提交者 <committer@example.com>"}),h={},$=S.branch(k||"main");h[k||"main"]=$,d.value.forEach(c=>{let f=$;if(c.branch){const b=ge(c.branch.split(",")[0]);h[b]||(h[b]=S.branch(b)),f=h[b]}f.commit({hash:c.hash,subject:c.message,author:`${c.author} <${c.email}>`})})}function z(){j.value=!j.value,j.value&&d.value.length>0&&setTimeout(R,0)}function L(){B(!g.value)}te(()=>{B()});const I=()=>B(g.value);return E({refreshLog:I}),(N,k)=>{const S=_e;return m(),y("div",Dt,[l("div",Et,[k[1]||(k[1]=l("h2",null,"提交历史",-1)),l("div",Mt,[s(x(K),{type:"primary",size:"small",onClick:z},{default:u(()=>[D(_(j.value?"表格视图":"图表视图"),1)]),_:1}),s(x(K),{type:"primary",size:"small",onClick:L,loading:a.value},{default:u(()=>[D(_(g.value?"显示最近100条":"显示所有提交"),1)]),_:1},8,["loading"]),s(x(K),{icon:x(Ie),circle:"",size:"small",onClick:k[0]||(k[0]=h=>I()),loading:a.value},null,8,["icon","loading"])])]),v.value?(m(),y("div",Pt,_(v.value),1)):(m(),y("div",Rt,[j.value?(m(),y("div",zt,[d.value.length>0?(m(),y("div",It," 显示 "+_(d.value.length)+" 条提交记录 "+_(g.value?"(全部)":"(最近100条)"),1)):H("",!0),l("div",{ref_key:"graphContainer",ref:A,class:"graph-container"},null,512)])):(m(),y("div",Ut,[d.value.length>0?(m(),y("div",Ft," 显示 "+_(d.value.length)+" 条提交记录 "+_(g.value?"(全部)":"(最近100条)"),1)):H("",!0),ye((m(),G(x(Fe),{data:d.value,style:{width:"100%"},stripe:"",border:""},{default:u(()=>[s(x(Q),{prop:"hash",label:"提交哈希",width:"100",resizable:""}),s(x(Q),{prop:"date",label:"日期",width:"180",resizable:""}),s(x(Q),{label:"作者",width:"200",resizable:""},{default:u(h=>[D(_(h.row.author)+" <"+_(h.row.email)+"> ",1)]),_:1}),s(x(Q),{label:"分支",width:"180",resizable:""},{default:u(h=>[h.row.branch?(m(),y("div",Gt,[(m(!0),y(J,null,q(h.row.branch.split(","),($,c)=>(m(),G(x(Ue),{key:c,size:"small",type:Ht($),class:"branch-tag"},{default:u(()=>[D(_(ge($)),1)]),_:2},1032,["type"]))),128))])):H("",!0)]),_:1}),s(x(Q),{prop:"message",label:"提交信息","min-width":"250"})]),_:1},8,["data"])),[[S,a.value]])]))]))])}}}),Wt=se(Jt,[["__scopeId","data-v-c6b721c7"]]),qt="data:image/svg+xml,%3csvg%20width='200'%20height='200'%20viewBox='0%200%20200%20200'%20xmlns='http://www.w3.org/2000/svg'%3e%3c!--%20主分支%20--%3e%3cline%20x1='40'%20y1='40'%20x2='40'%20y2='160'%20stroke='%2334495e'%20stroke-width='8'%20stroke-linecap='round'%20/%3e%3c!--%20特性分支%20--%3e%3cpath%20d='M40,70%20C60,70%2080,90%2080,110%20L80,130'%20stroke='%233498db'%20stroke-width='8'%20stroke-linecap='round'%20stroke-linejoin='round'%20fill='none'%20/%3e%3c!--%20开发分支%20--%3e%3cpath%20d='M40,100%20C60,100%20100,110%20100,130%20L100,160'%20stroke='%232ecc71'%20stroke-width='8'%20stroke-linecap='round'%20stroke-linejoin='round'%20fill='none'%20/%3e%3c!--%20修复分支%20--%3e%3cpath%20d='M40,130%20C60,130%20120,140%20120,160'%20stroke='%23e74c3c'%20stroke-width='8'%20stroke-linecap='round'%20stroke-linejoin='round'%20fill='none'%20/%3e%3c!--%20合并点%20--%3e%3cpath%20d='M80,130%20C80,145%2060,145%2040,145'%20stroke='%233498db'%20stroke-width='8'%20stroke-linecap='round'%20stroke-linejoin='round'%20fill='none'%20/%3e%3c!--%20节点%20--%3e%3ccircle%20cx='40'%20cy='40'%20r='10'%20fill='%2334495e'%20/%3e%3ccircle%20cx='40'%20cy='70'%20r='10'%20fill='%2334495e'%20/%3e%3ccircle%20cx='40'%20cy='100'%20r='10'%20fill='%2334495e'%20/%3e%3ccircle%20cx='40'%20cy='130'%20r='10'%20fill='%2334495e'%20/%3e%3ccircle%20cx='40'%20cy='145'%20r='10'%20fill='%2334495e'%20/%3e%3ccircle%20cx='40'%20cy='160'%20r='10'%20fill='%2334495e'%20/%3e%3ccircle%20cx='80'%20cy='130'%20r='10'%20fill='%233498db'%20/%3e%3ccircle%20cx='100'%20cy='160'%20r='10'%20fill='%232ecc71'%20/%3e%3ccircle%20cx='120'%20cy='160'%20r='10'%20fill='%23e74c3c'%20/%3e%3c/svg%3e",Kt={class:"main-header"},Zt={class:"header-left"},Qt=["src"],Xt={class:"header-info"},Yt={key:0,id:"user-info"},es={class:"user-name"},ts={class:"user-email"},ss={class:"container"},as={class:"layout-container"},ls={class:"left-panel"},os={class:"right-panel"},ns={class:"dialog-footer"},is={class:"main-footer"},rs={key:0,class:"branch-info"},cs=ee({__name:"App",setup(w){const E=o(""),d=o(null),v=o(null),a=o(""),g=o(""),T=o(""),j=o([]),A=o(!1);async function B(){try{const t=await(await fetch("/api/config/getConfig")).json();E.value=`默认提交信息: ${t.defaultCommitMessage}`}catch(n){console.error("加载配置失败:",n)}}async function R(){try{const t=await(await fetch("/api/branch")).json();t.branch&&(a.value=t.branch)}catch(n){console.error("获取分支信息失败:",n)}}async function z(){try{const t=await(await fetch("/api/branches")).json();t.branches&&Array.isArray(t.branches)&&(j.value=t.branches)}catch(n){console.error("获取所有分支信息失败:",n)}}async function L(n){console.log("切换到分支:",n);try{A.value=!0;const V=await(await fetch("/api/checkout",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({branch:n})})).json();V.success?(r({message:`已切换到分支: ${n}`,type:"success"}),R(),v.value&&v.value.refreshStatus(),d.value&&d.value.refreshLog()):(r({message:`切换分支失败: ${V.error}`,type:"error"}),a.value=a.value)}catch(t){r({message:`切换分支失败: ${t.message}`,type:"error"}),a.value=a.value}finally{A.value=!1}}async function I(){try{const t=await(await fetch("/api/user-info")).json();t.name&&t.email&&(g.value=t.name,T.value=t.email)}catch(n){console.error("获取用户信息失败:",n)}}te(()=>{B(),R(),z(),I()});function N(){d.value&&d.value.refreshLog(),v.value&&v.value.refreshStatus()}function k(){d.value&&d.value.refreshLog(),v.value&&v.value.refreshStatus(),R()}const S=o(!1),h=o(""),$=o(""),c=o(!1);async function f(){if(!h.value.trim()){r({message:"分支名称不能为空",type:"warning"});return}try{c.value=!0;const t=await(await fetch("/api/create-branch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({newBranchName:h.value,baseBranch:$.value||a.value})})).json();t.success?(r({message:`已创建并切换到分支: ${h.value}`,type:"success"}),S.value=!1,h.value="",R(),z(),v.value&&v.value.refreshStatus(),d.value&&d.value.refreshLog()):r({message:`创建分支失败: ${t.error}`,type:"error"})}catch(n){r({message:`创建分支失败: ${n.message}`,type:"error"})}finally{c.value=!1}}function b(){$.value=a.value,S.value=!0}return(n,t)=>{const V=we,C=Je,W=ke,X=be,ae=He,Z=K,le=re,oe=he;return m(),y(J,null,[l("header",Kt,[l("div",Zt,[l("img",{src:x(qt),alt:"Zen GitSync Logo",class:"logo"},null,8,Qt),t[5]||(t[5]=l("h1",null,"Zen GitSync UI",-1))]),l("div",Xt,[g.value&&T.value?(m(),y("div",Yt,[t[6]||(t[6]=l("span",{class:"user-label"},"用户:",-1)),l("span",es,_(g.value),1),l("span",ts,"<"+_(T.value)+">",1)])):H("",!0)])]),l("div",ss,[l("div",as,[l("div",ls,[s(ut,{ref_key:"gitStatusRef",ref:v},null,512)]),l("div",os,[s(At,{onCommitSuccess:N,onPushSuccess:k}),s(Wt,{ref_key:"logListRef",ref:d},null,512)]),s(le,{modelValue:S.value,"onUpdate:modelValue":t[3]||(t[3]=O=>S.value=O),title:"创建新分支",width:"30%","destroy-on-close":""},{footer:u(()=>[l("span",ns,[s(Z,{onClick:t[2]||(t[2]=O=>S.value=!1)},{default:u(()=>t[7]||(t[7]=[D("取消")])),_:1}),s(Z,{type:"primary",onClick:f,loading:c.value},{default:u(()=>t[8]||(t[8]=[D(" 创建 ")])),_:1},8,["loading"])])]),default:u(()=>[s(ae,{model:{newBranchName:h.value,selectedBaseBranch:$.value}},{default:u(()=>[s(C,{label:"新分支名称"},{default:u(()=>[s(V,{modelValue:h.value,"onUpdate:modelValue":t[0]||(t[0]=O=>h.value=O),placeholder:"请输入新分支名称"},null,8,["modelValue"])]),_:1}),s(C,{label:"基于分支"},{default:u(()=>[s(X,{modelValue:$.value,"onUpdate:modelValue":t[1]||(t[1]=O=>$.value=O),placeholder:"选择基础分支",style:{width:"100%"}},{default:u(()=>[(m(!0),y(J,null,q(j.value,O=>(m(),G(W,{key:O,label:O,value:O},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"])])]),l("footer",is,[a.value?(m(),y("div",rs,[t[9]||(t[9]=l("span",{class:"branch-label"},"当前分支:",-1)),s(X,{modelValue:a.value,"onUpdate:modelValue":t[4]||(t[4]=O=>a.value=O),size:"small",onChange:L,loading:A.value,class:"branch-select"},{default:u(()=>[(m(!0),y(J,null,q(j.value,O=>(m(),G(W,{key:O,label:O,value:O},null,8,["label","value"]))),128))]),_:1},8,["modelValue","loading"]),s(Z,{type:"primary",size:"small",onClick:b,style:{"margin-left":"5px"}},{default:u(()=>[s(oe,null,{default:u(()=>[s(x(We))]),_:1})]),_:1})])):H("",!0),t[10]||(t[10]=l("div",{class:"footer-right"},null,-1))])],64)}}}),us=se(cs,[["__scopeId","data-v-93cc73d5"]]),ds=qe(us);ds.mount("#app");
@@ -1 +0,0 @@
1
- .status-header[data-v-849f0626]{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.status-header h2[data-v-849f0626]{margin:0}.file-list[data-v-849f0626]{margin-top:10px}.file-item[data-v-849f0626]{display:flex;align-items:center;margin-bottom:4px;padding:2px 6px;border-radius:3px;font-size:14px;cursor:pointer;transition:opacity .2s}.file-item[data-v-849f0626]:hover{opacity:.8}.file-item.added[data-v-849f0626]{background:#e6ffed;color:#22863a}.file-item.modified[data-v-849f0626]{background:#fff5b1;color:#b08800}.file-item.deleted[data-v-849f0626]{background:#ffeef0;color:#cb2431}.file-item.untracked[data-v-849f0626]{background:#f1f8ff;color:#0366d6}.file-type[data-v-849f0626]{font-weight:700;margin-right:8px}.file-path[data-v-849f0626]{font-family:monospace}.diff-content[data-v-849f0626]{max-height:70vh;overflow-y:auto;background-color:#f6f8fa;border:1px solid #e1e4e8;border-radius:3px;padding:15px;font-family:monospace;white-space:pre-wrap}.diff-formatted[data-v-849f0626]{margin:0}[data-v-849f0626] .diff-header{color:#24292e;font-weight:700}[data-v-849f0626] .diff-old-file{color:#cb2431;background-color:#ffeef0}[data-v-849f0626] .diff-new-file{color:#22863a;background-color:#e6ffed}[data-v-849f0626] .diff-hunk-header{color:#6f42c1;background-color:#f1f8ff}[data-v-849f0626] .diff-added{color:#22863a;background-color:#e6ffed}[data-v-849f0626] .diff-removed{color:#cb2431;background-color:#ffeef0}[data-v-849f0626] .diff-context{color:#24292e}.no-diff[data-v-849f0626]{text-align:center;padding:20px;color:#666}.file-navigation[data-v-849f0626]{display:flex;justify-content:center;align-items:center;margin-top:15px;gap:10px}.file-counter[data-v-849f0626]{font-size:14px;color:#606266}.current-directory[data-v-849f0626]{padding:10px 15px;background-color:#f0f0f0;border-bottom:1px solid #e1e4e8;display:flex;align-items:center;gap:8px;font-family:monospace}.not-git-repo[data-v-849f0626]{margin:10px 0;padding:10px;background-color:#fffbf6;border:1px solid #f0c78a;border-radius:4px}.commit-form[data-v-00e49634]{display:flex;margin-bottom:15px;gap:10px}.button-group[data-v-00e49634]{display:flex;gap:10px}.commit-mode-toggle[data-v-00e49634]{margin-bottom:15px}.standard-commit-form[data-v-00e49634]{display:flex;flex-direction:column;gap:15px;margin-bottom:15px}.standard-commit-header[data-v-00e49634]{display:flex;gap:10px;width:100%}.type-select[data-v-00e49634]{width:120px;flex-shrink:0}.scope-container[data-v-00e49634]{display:flex;align-items:center;gap:5px;flex-grow:0;width:200px}.scope-input[data-v-00e49634]{flex-grow:1}.description-container[data-v-00e49634]{display:flex;align-items:center;gap:5px;flex-grow:1}.description-input[data-v-00e49634]{flex-grow:1;min-width:200px}.settings-button[data-v-00e49634]{flex-shrink:0}.preview-section[data-v-00e49634]{background-color:#f5f7fa;padding:10px;border-radius:4px}.preview-title[data-v-00e49634]{font-weight:700;margin-bottom:5px}.preview-content[data-v-00e49634]{white-space:pre-wrap;font-family:monospace;margin:0;padding:10px;background-color:#ebeef5;border-radius:4px}.template-container[data-v-00e49634]{display:flex;flex-direction:column;height:calc(85vh - 100px);overflow-y:auto}.template-form[data-v-00e49634]{margin-bottom:20px}.template-list[data-v-00e49634]{flex:1;overflow-y:auto}.template-input[data-v-00e49634]{flex-grow:1}.template-list[data-v-00e49634]{overflow-y:auto;height:100%}.template-item[data-v-00e49634]{margin-bottom:10px}.template-item[data-v-00e49634]:hover{background-color:#f5f7fa}.template-content[data-v-00e49634]{flex-grow:1;margin-right:10px;word-break:break-all}.template-actions[data-v-00e49634]{display:flex;gap:5px;justify-content:flex-end;min-width:120px;flex-shrink:0}.log-header[data-v-c6b721c7]{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.log-actions[data-v-c6b721c7]{display:flex;gap:8px}.branch-container[data-v-c6b721c7]{display:flex;flex-wrap:wrap;gap:4px}.branch-tag[data-v-c6b721c7]{margin-right:4px}.commit-count[data-v-c6b721c7]{margin-bottom:10px;font-size:14px;color:#606266;text-align:right}.graph-container[data-v-c6b721c7]{width:100%;height:600px;overflow:auto;border:1px solid #ebeef5;border-radius:4px;padding:10px;background-color:#fff}.graph-view[data-v-c6b721c7]{width:100%}body{font-family:Arial,sans-serif;margin:0;padding:0;background-color:#f5f5f5}.container{margin:0 auto;padding:20px 30px}.main-header{background-color:#24292e;color:#fff;padding:15px 20px;display:flex;justify-content:space-between;align-items:center}.header-left{display:flex;align-items:center;gap:10px}.logo{height:32px;width:auto}h1{margin:0;font-size:24px}.header-info{display:flex;flex-direction:column;align-items:flex-end;gap:5px}#branch-info,#user-info{background-color:#ffffff1a;padding:4px 8px;border-radius:4px;font-size:14px}.branch-label,.user-label,.user-name{font-weight:700;margin-right:5px}.user-email{color:#e0e0e0}.branch-name{font-family:monospace}.card{background-color:#fff;border-radius:5px;box-shadow:0 2px 5px #0000001a;margin-bottom:20px;padding:20px}.status-box{background-color:#f6f8fa;border:1px solid #e1e4e8;border-radius:3px;padding:15px;white-space:pre-wrap;font-family:monospace;max-height:300px;overflow-y:auto}.layout-container{display:flex;gap:20px}.left-panel{flex:0 0 30%;max-width:30%}.right-panel{flex:0 0 70%;max-width:70%}@media (max-width: 768px){.layout-container{flex-direction:column}.left-panel,.right-panel{flex:0 0 100%;max-width:100%}.header-left{gap:8px}.logo{height:24px}h1{font-size:20px}}.commit-form{display:flex;margin-bottom:15px}.log-item{padding:10px 0;border-bottom:1px solid #eee}.log-item:last-child{border-bottom:none}.log-hash{color:#6f42c1;font-family:monospace}.log-author,.log-date{color:#6a737d}.log-message{font-weight:700}.log-branch{display:inline-block;background-color:#0366d6;color:#fff;border-radius:3px;padding:2px 6px;margin-left:8px;font-size:12px}.branch-select{width:150px;margin-left:5px}.branch-select :deep(.el-input__inner){background-color:#ffffff1a;color:#fff;border:none}.branch-select :deep(.el-input__suffix){color:#fff}.logo[data-v-93cc73d5]{will-change:filter;transition:filter .3s}.logo[data-v-93cc73d5]:hover{filter:drop-shadow(0 0 2em #42b883aa)}.main-footer[data-v-93cc73d5]{background-color:#24292e;color:#fff;padding:10px 20px;display:flex;justify-content:space-between;align-items:center;position:fixed;bottom:0;left:0;right:0;z-index:100}.branch-info[data-v-93cc73d5]{display:flex;align-items:center}.footer-right[data-v-93cc73d5]{color:#ffffffb3;font-size:12px}