<script setup>
import { ref, computed, watch, h, onErrorCaptured, onBeforeUnmount } from 'vue'
import {
  SyncOutlined,
  CloudDownloadOutlined,
  CloudUploadOutlined,
  ReloadOutlined,
  FileOutlined,
  CheckCircleOutlined,
  PlusOutlined,
  MinusOutlined,
  UndoOutlined,
  CaretDownOutlined,
  CaretRightOutlined,
  BranchesOutlined,
  HistoryOutlined,
  EditOutlined,
  SwapOutlined,
  UserOutlined,
  RollbackOutlined,
  ExclamationCircleOutlined,
  CloseCircleOutlined,
  TagOutlined,
  TagsOutlined,
  CloudUploadOutlined as PushTagIcon,
  DeleteOutlined,
  SendOutlined,
  MergeCellsOutlined,
  InboxOutlined,
  SettingOutlined,
  GlobalOutlined,
  SaveOutlined,
  DownloadOutlined,
  SearchOutlined,
  WarningOutlined,
  StopOutlined,
} from '@ant-design/icons-vue'
import { Modal, message } from 'ant-design-vue'
import { AppService } from '../../bindings/github.com/zhuy1228/GitPilot/internal/app'
import FileTreeNode from './FileTreeNode.vue'
import CommitFileTreeNode from './CommitFileTreeNode.vue'

const props = defineProps({
  project: { type: Object, default: null }
})

const status = ref(null)
const changedFiles = ref([])
const loadingBase = ref(false)
const loadingFiles = ref(false)
const errorMsg = ref('')
const renderError = ref('')
const selectedFile = ref(null)
const fileContent = ref('')
const fileDiff = ref('')
const viewMode = ref('diff')
const actionLoading = ref('')
const collapsedDirs = ref(new Set())
const commitMessage = ref('')
const commitLoading = ref(false)
const stagedCollapsed = ref(false)
const unstagedCollapsed = ref(false)

// ---- 分支 & 提交历史 ----
const activeTab = ref('changes') // 'changes' | 'history'
const branches = ref([])
const branchLoading = ref(false)
const showBranchDropdown = ref(false)
const commitLogs = ref([])
const commitLogsLoading = ref(false)
const selectedCommit = ref(null)
const commitDiff = ref('')
const resetLoading = ref(false)
const commitFiles = ref([])
const commitFilesLoading = ref(false)
const selectedCommitFile = ref(null)
const commitFileDiff = ref('')
const commitCollapsedDirs = ref(new Set())

// ---- 标签管理 ----
const tags = ref([])
const tagsLoading = ref(false)
const showCreateTag = ref(false)
const newTagName = ref('')
const newTagMessage = ref('')
const createTagLoading = ref(false)

// ---- Release 同步 ----
const releases = ref([])
const releasesLoading = ref(false)
const selectedRelease = ref(null)
const releaseSyncSource = ref('')
const releaseSyncTarget = ref('')
const releaseSyncLoading = ref(false)
const releaseSyncResults = ref([])
const releaseSrcToken = ref('')
const releaseTgtToken = ref('')

// ---- 分支管理 ----
const newBranchName = ref('')
const createBranchLoading = ref(false)
const remoteBranches = ref([])

// ---- Remote 远程仓库 ----
const remotes = ref([])
const currentRemote = ref('origin')
const showRemoteDropdown = ref(false)
const showAddRemote = ref(false)
const newRemoteName = ref('')
const newRemoteUrl = ref('')
const addRemoteLoading = ref(false)

// ---- Stash 贮藏管理 ----
const stashList = ref([])
const stashLoading = ref(false)
const showStashSave = ref(false)
const stashMessage = ref('')
const stashSaveLoading = ref(false)
const showStashPanel = ref(false)

// ---- 设置 ----
const showSettings = ref(false)
const settingsLoading = ref(false)
const gitConfig = ref({ userName: '', userEmail: '' })

// ---- 代理开关 ----
const projectProxy = ref(null) // null: 跟随全局, true: 强制使用, false: 强制不使用

// ---- 冲突处理 ----
const isMerging = ref(false)
const conflictFiles = ref([])
const selectedConflictFile = ref(null)
const conflictContent = ref('')
const conflictSaving = ref(false)

// ---- 提交搜索 ----
const searchKeyword = ref('')
const searchAuthor = ref('')
const searchLoading = ref(false)
const isSearchMode = ref(false)
const searchResults = ref([])
const displayLogs = computed(() => isSearchMode.value ? searchResults.value : commitLogs.value)

// ---- 文件列表拖拽调整宽度 ----
const fileListWidth = ref(300)
const MIN_FILELIST = 180
const MAX_FILELIST = 600
let draggingFileList = false

function startFileListResize(e) {
  e.preventDefault()
  draggingFileList = true
  const startX = e.clientX
  const startW = fileListWidth.value

  function onMove(ev) {
    if (!draggingFileList) return
    const delta = ev.clientX - startX
    fileListWidth.value = Math.min(MAX_FILELIST, Math.max(MIN_FILELIST, startW + delta))
  }
  function onUp() {
    draggingFileList = false
    document.removeEventListener('mousemove', onMove)
    document.removeEventListener('mouseup', onUp)
    document.body.style.cursor = ''
    document.body.style.userSelect = ''
  }
  document.body.style.cursor = 'col-resize'
  document.body.style.userSelect = 'none'
  document.addEventListener('mousemove', onMove)
  document.addEventListener('mouseup', onUp)
}

onBeforeUnmount(() => {
  draggingFileList = false
})

// 分离暂存区和工作区文件
const stagedFiles = computed(() => changedFiles.value.filter(f => f.staged))
const unstagedFiles = computed(() => changedFiles.value.filter(f => !f.staged))

// 将扁平文件列表构建为目录树
function buildTree(files) {
  if (!files.length) return []

  const root = { name: '', children: {}, files: [] }

  for (const file of files) {
    const parts = file.filePath.split('/')
    let current = root
    for (let i = 0; i < parts.length - 1; i++) {
      const dirName = parts[i]
      if (!current.children[dirName]) {
        current.children[dirName] = { name: dirName, children: {}, files: [] }
      }
      current = current.children[dirName]
    }
    current.files.push(file)
  }

  function toArray(node, parentPath) {
    const result = []
    const dirs = Object.values(node.children).sort((a, b) => a.name.localeCompare(b.name))
    for (const dir of dirs) {
      const dirPath = parentPath ? parentPath + '/' + dir.name : dir.name
      const children = toArray(dir, dirPath)
      const fileCount = countFiles(dir)
      result.push({ type: 'dir', name: dir.name, path: dirPath, children, fileCount })
    }
    const sortedFiles = [...node.files].sort((a, b) => {
      const nameA = a.filePath.split('/').pop()
      const nameB = b.filePath.split('/').pop()
      return nameA.localeCompare(nameB)
    })
    for (const f of sortedFiles) {
      const fileName = f.filePath.split('/').pop()
      result.push({ type: 'file', name: fileName, data: f })
    }
    return result
  }

  function countFiles(node) {
    let count = node.files.length
    for (const child of Object.values(node.children)) {
      count += countFiles(child)
    }
    return count
  }

  return toArray(root, '')
}

const stagedTree = computed(() => buildTree(stagedFiles.value))
const unstagedTree = computed(() => buildTree(unstagedFiles.value))

function toggleDir(dirPath) {
  const s = new Set(collapsedDirs.value)
  if (s.has(dirPath)) {
    s.delete(dirPath)
  } else {
    s.add(dirPath)
  }
  collapsedDirs.value = s
}

// 捕获子组件/自身渲染错误
onErrorCaptured((err) => {
  renderError.value = String(err)
  console.error('[ContentArea] 渲染错误:', err)
  return false
})

// 分步异步加载
async function loadStatus() {
  if (!props.project?.path) return
  loadingBase.value = true
  loadingFiles.value = false
  errorMsg.value = ''
  renderError.value = ''
  status.value = null
  changedFiles.value = []
  selectedFile.value = null
  fileContent.value = ''
  fileDiff.value = ''

  // 第一步：快速获取分支+远程URL
  try {
    const result = await AppService.GetProjectStatus(props.project.path)
    // 防御 Wails 绑定可能返回 null/undefined
    if (result && typeof result === 'object') {
      status.value = {
        branch: result.branch || '',
        remoteUrl: result.remoteUrl || result.remoteURL || '',
        remotes: Array.isArray(result.remotes) ? result.remotes : [],
        currentRemote: result.currentRemote || 'origin',
        changedFiles: [],
      }
      // 同步 remotes
      remotes.value = status.value.remotes
      if (remotes.value.length && !remotes.value.find(r => r.name === currentRemote.value)) {
        currentRemote.value = remotes.value[0].name
      }
      // 同步代理设置
      projectProxy.value = result.useProxy ?? null
    } else {
      errorMsg.value = '获取项目状态返回空值: ' + JSON.stringify(result)
      loadingBase.value = false
      return
    }
  } catch (e) {
    errorMsg.value = '获取项目状态失败: ' + String(e)
    loadingBase.value = false
    return
  }
  loadingBase.value = false

  // 第二步：异步加载变更文件（可能较慢）
  loadingFiles.value = true
  try {
    const files = await AppService.GetProjectChangedFiles(props.project.path)
    changedFiles.value = Array.isArray(files) ? files : []
  } catch (e) {
    console.error('获取变更文件失败:', e)
    // 不阻塞主渲染，仅文件列表显示异常
  } finally {
    loadingFiles.value = false
  }

  // 检查合并冲突状态
  try {
    isMerging.value = await AppService.IsMerging(props.project.path)
    if (isMerging.value) {
      const cFiles = await AppService.GetConflictFiles(props.project.path)
      conflictFiles.value = Array.isArray(cFiles) ? cFiles : []
    } else {
      conflictFiles.value = []
      selectedConflictFile.value = null
    }
  } catch (e) {
    console.error('检查合并状态失败:', e)
  }

  // 第三步：并行加载分支列表 + 提交历史
  loadBranches()
  if (activeTab.value === 'history') {
    loadCommitLog()
  }
}

function isNewFile(file) {
  return file && (file.status === 'A' || file.status === '?')
}

const binaryExts = new Set([
  '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.svg',
  '.mp3', '.mp4', '.avi', '.mov', '.wav', '.flac', '.ogg',
  '.zip', '.tar', '.gz', '.bz2', '.7z', '.rar', '.xz',
  '.exe', '.dll', '.so', '.dylib', '.bin', '.dat',
  '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
  '.woff', '.woff2', '.ttf', '.otf', '.eot',
  '.class', '.pyc', '.o', '.a', '.lib',
  '.sqlite', '.db', '.mdb',
  '.icns',
])

function isBinaryFile(filePath) {
  if (!filePath) return false
  const ext = '.' + filePath.split('.').pop().toLowerCase()
  return binaryExts.has(ext)
}

async function selectFile(file) {
  selectedFile.value = file
  fileContent.value = ''
  fileDiff.value = ''

  // 二进制文件直接跳过读取
  if (isBinaryFile(file.filePath)) {
    viewMode.value = 'content'
    fileContent.value = '[二进制文件，不支持预览]'
    fileDiff.value = '[二进制文件，不支持预览]'
    return
  }

  if (isNewFile(file)) {
    // 新增/未跟踪文件：先获取全部内容，用内容模拟 diff
    viewMode.value = 'content'
    try {
      const content = await AppService.GetFileContent(props.project.path, file.filePath)
      fileContent.value = content
      const lines = content.split('\n')
      fileDiff.value = `--- /dev/null\n+++ b/${file.filePath}\n@@ -0,0 +1,${lines.length} @@\n` + lines.map(l => '+' + l).join('\n')
    } catch (e) {
      fileContent.value = '读取文件失败: ' + e
      fileDiff.value = '(新增文件，读取失败)'
    }
  } else {
    // 已有文件：根据是否暂存选择不同的 diff API
    viewMode.value = 'diff'
    try {
      const diff = file.staged
        ? await AppService.GetFileDiffStaged(props.project.path, file.filePath)
        : await AppService.GetFileDiff(props.project.path, file.filePath)
      fileDiff.value = diff || '(无差异)'
    } catch (e) {
      fileDiff.value = '获取 diff 失败: ' + e
    }
    try {
      const content = await AppService.GetFileContent(props.project.path, file.filePath)
      fileContent.value = content
    } catch (e) {
      fileContent.value = '读取文件失败: ' + e
    }
  }
}

// ---- 暂存/取消暂存/丢弃操作 ----

// 静默刷新变更文件列表（不触发 loading / 骨架屏，保留选中状态）
async function refreshFiles() {
  try {
    const files = await AppService.GetProjectChangedFiles(props.project.path)
    changedFiles.value = Array.isArray(files) ? files : []

    // 保持选中文件的引用同步：用最新数据中的同路径+同staged替换
    if (selectedFile.value) {
      const sel = selectedFile.value
      const match = changedFiles.value.find(
        f => f.filePath === sel.filePath && f.staged === sel.staged
      )
      if (!match) {
        // 如果原来选中的文件 staged 态变了（比如刚暂存），尝试找另一种态
        const altMatch = changedFiles.value.find(f => f.filePath === sel.filePath)
        if (altMatch) {
          selectedFile.value = altMatch
        } else {
          // 文件已不在变更列表中（比如被丢弃了），清空右侧
          selectedFile.value = null
          fileContent.value = ''
          fileDiff.value = ''
        }
      } else {
        selectedFile.value = match
      }
    }
  } catch (e) {
    console.error('刷新变更文件失败:', e)
  }
}

// 从文件列表中收集指定目录下的所有文件路径
function collectFilesInDir(dirPath, fileList) {
  return fileList.filter(f => f.filePath.startsWith(dirPath + '/') || f.filePath === dirPath).map(f => f.filePath)
}

async function stageFile(file) {
  try {
    await AppService.StageFiles(props.project.path, [file.filePath])
    await refreshFiles()
  } catch (e) {
    console.error('暂存失败:', e)
  }
}

async function unstageFile(file) {
  try {
    await AppService.UnstageFiles(props.project.path, [file.filePath])
    await refreshFiles()
  } catch (e) {
    console.error('取消暂存失败:', e)
  }
}

async function discardFile(file) {
  try {
    await AppService.DiscardFiles(props.project.path, [file.filePath])
    await refreshFiles()
  } catch (e) {
    console.error('丢弃更改失败:', e)
  }
}

async function stageDir(dirPath) {
  const paths = collectFilesInDir(dirPath, unstagedFiles.value)
  if (!paths.length) return
  try {
    await AppService.StageFiles(props.project.path, paths)
    await refreshFiles()
  } catch (e) {
    console.error('暂存目录失败:', e)
  }
}

async function unstageDir(dirPath) {
  const paths = collectFilesInDir(dirPath, stagedFiles.value)
  if (!paths.length) return
  try {
    await AppService.UnstageFiles(props.project.path, paths)
    await refreshFiles()
  } catch (e) {
    console.error('取消暂存目录失败:', e)
  }
}

async function discardDir(dirPath) {
  const paths = collectFilesInDir(dirPath, unstagedFiles.value)
  if (!paths.length) return
  try {
    await AppService.DiscardFiles(props.project.path, paths)
    await refreshFiles()
  } catch (e) {
    console.error('丢弃目录更改失败:', e)
  }
}

async function stageAll() {
  try {
    await AppService.StageAll(props.project.path)
    await refreshFiles()
  } catch (e) {
    console.error('暂存全部失败:', e)
  }
}

async function unstageAll() {
  try {
    await AppService.UnstageAll(props.project.path)
    await refreshFiles()
  } catch (e) {
    console.error('取消暂存全部失败:', e)
  }
}

async function discardAll() {
  // 丢弃所有未暂存的更改
  const paths = unstagedFiles.value.map(f => f.filePath)
  if (!paths.length) return
  try {
    await AppService.DiscardFiles(props.project.path, paths)
    await refreshFiles()
  } catch (e) {
    console.error('丢弃全部更改失败:', e)
  }
}

async function commitChanges() {
  if (!commitMessage.value.trim()) return
  commitLoading.value = true
  try {
    await AppService.CommitChanges(props.project.path, commitMessage.value.trim())
    commitMessage.value = ''
    await refreshFiles()
    // 刷新提交历史
    loadCommitLog()
  } catch (e) {
    console.error('提交失败:', e)
  } finally {
    commitLoading.value = false
  }
}

async function gitAction(action) {
  if (!props.project?.path) return
  actionLoading.value = action
  try {
    if (action === 'pull') {
      await AppService.PullProject(props.project.path, currentRemote.value)
    } else if (action === 'push') {
      await AppService.PushProject(props.project.path, currentRemote.value)
    } else {
      await AppService.FetchProject(props.project.path, currentRemote.value)
    }
    await loadStatus()
  } catch (e) {
    console.error(`${action} 失败:`, e)
  } finally {
    actionLoading.value = ''
  }
}

// ---- 多端推送 ----
async function pushToAllRemotes() {
  if (!props.project?.path) return
  actionLoading.value = 'pushAll'
  try {
    const results = await AppService.PushToAllRemotes(props.project.path)
    const successCount = results.filter(r => r.success).length
    const failCount = results.length - successCount
    if (failCount === 0) {
      message.success(`已成功推送到全部 ${successCount} 个远程仓库`)
    } else {
      Modal.info({
        title: '多端推送结果',
        content: h('div', results.map(r =>
          h('div', { style: `padding: 4px 0; color: ${r.success ? 'var(--success, #a6e3a1)' : '#f38ba8'}` }, [
            h('span', { style: 'font-weight: 600; margin-right: 8px;' }, r.remote + ':'),
            h('span', r.success ? '✓ 成功' : '✗ ' + r.message),
          ])
        )),
      })
    }
    await loadStatus()
  } catch (e) {
    console.error('多端推送失败:', e)
    Modal.error({ title: '多端推送失败', content: String(e) })
  } finally {
    actionLoading.value = ''
  }
}

async function pushTagToAllRemotes(tag) {
  if (!props.project?.path) return
  try {
    const results = await AppService.PushTagToAllRemotes(props.project.path, tag.name)
    const successCount = results.filter(r => r.success).length
    const failCount = results.length - successCount
    if (failCount === 0) {
      message.success(`标签 ${tag.name} 已推送到全部 ${successCount} 个远程仓库`)
    } else {
      Modal.info({
        title: '标签多端推送结果',
        content: h('div', results.map(r =>
          h('div', { style: `padding: 4px 0; color: ${r.success ? 'var(--success, #a6e3a1)' : '#f38ba8'}` }, [
            h('span', { style: 'font-weight: 600; margin-right: 8px;' }, r.remote + ':'),
            h('span', r.success ? '✓ 成功' : '✗ ' + r.message),
          ])
        )),
      })
    }
  } catch (e) {
    console.error('标签多端推送失败:', e)
    Modal.error({ title: '推送失败', content: String(e) })
  }
}

// ---- 代理开关 ----
async function toggleProxy(checked) {
  if (!props.project?.path) return
  const useProxy = checked ? true : false
  try {
    await AppService.SetProjectProxy(props.project.path, useProxy)
    projectProxy.value = useProxy
  } catch (e) {
    console.error('设置代理失败:', e)
    Modal.error({ title: '设置代理失败', content: String(e) })
  }
}

async function resetProxy() {
  if (!props.project?.path) return
  try {
    await AppService.SetProjectProxy(props.project.path, null)
    projectProxy.value = null
    message.success('已恢复跟随全局设置')
  } catch (e) {
    console.error('重置代理失败:', e)
  }
}

function getStatusColor(status) {
  const colors = { 'M': 'orange', 'A': 'green', 'D': 'red', '?': 'default' }
  return colors[status] || 'default'
}

function formatDiff(diff) {
  if (!diff) return ''
  return diff.split('\n').map(line => {
    if (line.startsWith('+') && !line.startsWith('+++')) {
      return `<span class="diff-add">${escapeHtml(line)}</span>`
    } else if (line.startsWith('-') && !line.startsWith('---')) {
      return `<span class="diff-del">${escapeHtml(line)}</span>`
    } else if (line.startsWith('@@')) {
      return `<span class="diff-hunk">${escapeHtml(line)}</span>`
    }
    return escapeHtml(line)
  }).join('\n')
}

function escapeHtml(text) {
  return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}

watch(() => props.project, loadStatus, { immediate: true })

// 切换到历史标签时加载提交历史
watch(activeTab, (tab) => {
  if (tab === 'history' && !commitLogs.value.length && !commitLogsLoading.value) {
    loadCommitLog()
  }
})

// ---- 分支管理 ----
async function loadBranches() {
  if (!props.project?.path) return
  try {
    const list = await AppService.GetBranches(props.project.path)
    branches.value = Array.isArray(list) ? list : []
  } catch (e) {
    console.error('获取分支失败:', e)
  }
  loadRemoteBranches()
  loadStashList()
}

async function switchBranch(branchName) {
  if (!props.project?.path) return
  branchLoading.value = true
  showBranchDropdown.value = false
  try {
    await AppService.SwitchBranch(props.project.path, branchName)
    await loadStatus()
  } catch (e) {
    console.error('切换分支失败:', e)
  } finally {
    branchLoading.value = false
  }
}

async function createBranch() {
  if (!props.project?.path || !newBranchName.value.trim()) return
  createBranchLoading.value = true
  try {
    await AppService.CreateBranch(props.project.path, newBranchName.value.trim())
    newBranchName.value = ''
    await loadBranches()
    message.success('分支创建成功')
  } catch (e) {
    console.error('创建分支失败:', e)
    Modal.error({ title: '创建分支失败', content: String(e) })
  } finally {
    createBranchLoading.value = false
  }
}

async function deleteBranch(branchName, force = false) {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认删除分支',
    icon: h(ExclamationCircleOutlined),
    content: h('div', [
      h('p', `确定要删除分支吗？`),
      h('p', { style: 'font-family: monospace; color: #89b4fa; font-size: 15px;' }, branchName),
    ]),
    okText: '删除',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.DeleteBranch(props.project.path, branchName, force)
        await loadBranches()
        message.success(`分支 ${branchName} 已删除`)
      } catch (e) {
        // 如果安全删除失败，提示是否强制删除
        if (!force && String(e).includes('not fully merged')) {
          Modal.confirm({
            title: '分支未完全合并',
            icon: h(ExclamationCircleOutlined),
            content: h('div', [
              h('p', `分支 ${branchName} 尚未完全合并到当前分支。`),
              h('p', { style: 'color: #f38ba8;' }, '是否强制删除？'),
            ]),
            okText: '强制删除',
            cancelText: '取消',
            okButtonProps: { danger: true },
            async onOk() {
              await deleteBranch(branchName, true)
            },
          })
        } else {
          Modal.error({ title: '删除分支失败', content: String(e) })
        }
      }
    },
  })
}

async function mergeBranch(branchName) {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认合并分支',
    icon: h(MergeCellsOutlined),
    content: h('div', [
      h('p', [
        '将 ',
        h('span', { style: 'font-family: monospace; color: #89b4fa;' }, branchName),
        ' 合并到 ',
        h('span', { style: 'font-family: monospace; color: #a6e3a1;' }, status.value?.branch || '当前分支'),
      ]),
    ]),
    okText: '合并',
    cancelText: '取消',
    async onOk() {
      try {
        const result = await AppService.MergeBranch(props.project.path, branchName)
        message.success('合并成功')
        await loadStatus()
      } catch (e) {
        console.error('合并分支失败:', e)
        Modal.error({ title: '合并失败', content: String(e) })
      }
    },
  })
}

// ---- 远程分支 ----
async function loadRemoteBranches() {
  if (!props.project?.path) return
  try {
    const list = await AppService.GetRemoteBranches(props.project.path, currentRemote.value)
    remoteBranches.value = Array.isArray(list) ? list : []
  } catch (e) {
    console.error('获取远程分支失败:', e)
  }
}

async function checkoutRemoteBranch(remoteBranch) {
  if (!props.project?.path) return
  showBranchDropdown.value = false
  try {
    await AppService.CheckoutRemoteBranch(props.project.path, remoteBranch)
    message.success('检出成功')
    await loadStatus()
  } catch (e) {
    console.error('检出远程分支失败:', e)
    Modal.error({ title: '检出失败', content: String(e) })
  }
}

async function deleteRemoteBranch(remoteBranch) {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认删除远程分支',
    icon: h(ExclamationCircleOutlined),
    content: h('div', [
      h('p', '确定要删除远程分支吗？此操作不可逆！'),
      h('p', { style: 'font-family: monospace; color: #f38ba8; font-size: 15px;' }, remoteBranch),
    ]),
    okText: '删除',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.DeleteRemoteBranch(props.project.path, remoteBranch, currentRemote.value)
        await loadRemoteBranches()
        message.success(`远程分支 ${remoteBranch} 已删除`)
      } catch (e) {
        Modal.error({ title: '删除远程分支失败', content: String(e) })
      }
    },
  })
}

// ---- Stash 贮藏管理 ----
async function loadStashList() {
  if (!props.project?.path) return
  stashLoading.value = true
  try {
    const list = await AppService.GetStashList(props.project.path)
    stashList.value = Array.isArray(list) ? list : []
  } catch (e) {
    console.error('获取贮藏列表失败:', e)
    stashList.value = []
  } finally {
    stashLoading.value = false
  }
}

async function stashSave() {
  if (!props.project?.path) return
  stashSaveLoading.value = true
  try {
    await AppService.StashSave(props.project.path, stashMessage.value.trim())
    stashMessage.value = ''
    showStashSave.value = false
    message.success('已贮藏当前变更')
    await refreshFiles()
    await loadStashList()
  } catch (e) {
    console.error('贮藏失败:', e)
    Modal.error({ title: '贮藏失败', content: String(e) })
  } finally {
    stashSaveLoading.value = false
  }
}

async function stashApply(index) {
  if (!props.project?.path) return
  try {
    await AppService.StashApply(props.project.path, index)
    message.success('已应用贮藏')
    await refreshFiles()
  } catch (e) {
    Modal.error({ title: '应用贮藏失败', content: String(e) })
  }
}

async function stashPop(index) {
  if (!props.project?.path) return
  try {
    await AppService.StashPop(props.project.path, index)
    message.success('已应用并删除贮藏')
    await refreshFiles()
    await loadStashList()
  } catch (e) {
    Modal.error({ title: '应用贮藏失败', content: String(e) })
  }
}

async function stashDrop(index) {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认删除贮藏',
    icon: h(ExclamationCircleOutlined),
    content: `确定要删除 stash@{${index}} 吗？`,
    okText: '删除',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.StashDrop(props.project.path, index)
        message.success('已删除贮藏')
        await loadStashList()
      } catch (e) {
        Modal.error({ title: '删除贮藏失败', content: String(e) })
      }
    },
  })
}

// ---- 设置 ----
async function openSettings() {
  showSettings.value = true
  settingsLoading.value = true
  try {
    const config = await AppService.GetGitGlobalConfig()
    gitConfig.value = {
      userName: config?.userName || '',
      userEmail: config?.userEmail || '',
    }
  } catch (e) {
    console.error('获取 Git 配置失败:', e)
  } finally {
    settingsLoading.value = false
  }
}

async function saveSettings() {
  settingsLoading.value = true
  try {
    await AppService.SetGitGlobalConfig(gitConfig.value.userName, gitConfig.value.userEmail)
    message.success('设置已保存')
    showSettings.value = false
  } catch (e) {
    Modal.error({ title: '保存设置失败', content: String(e) })
  } finally {
    settingsLoading.value = false
  }
}

// ---- 冲突处理 ----
async function selectConflictFile(file) {
  selectedConflictFile.value = file
  conflictContent.value = ''
  try {
    const content = await AppService.GetConflictFileContent(props.project.path, file.filePath)
    conflictContent.value = content
  } catch (e) {
    conflictContent.value = '读取冲突文件失败: ' + e
  }
}

async function saveConflictContent() {
  if (!selectedConflictFile.value) return
  conflictSaving.value = true
  try {
    await AppService.SaveConflictFile(props.project.path, selectedConflictFile.value.filePath, conflictContent.value)
    message.success('冲突文件已保存')
  } catch (e) {
    Modal.error({ title: '保存失败', content: String(e) })
  } finally {
    conflictSaving.value = false
  }
}

async function resolveConflictFiles(files) {
  if (!props.project?.path) return
  try {
    const paths = files.map(f => f.filePath)
    await AppService.ResolveConflictFile(props.project.path, paths)
    message.success('已标记为已解决')
    isMerging.value = await AppService.IsMerging(props.project.path)
    if (isMerging.value) {
      const cFiles = await AppService.GetConflictFiles(props.project.path)
      conflictFiles.value = Array.isArray(cFiles) ? cFiles : []
    } else {
      conflictFiles.value = []
    }
    selectedConflictFile.value = null
    await refreshFiles()
  } catch (e) {
    Modal.error({ title: '解决冲突失败', content: String(e) })
  }
}

async function completeMerge() {
  if (!props.project?.path) return
  const msg = commitMessage.value.trim() || 'Merge completed'
  commitLoading.value = true
  try {
    await AppService.CommitChanges(props.project.path, msg)
    commitMessage.value = ''
    isMerging.value = false
    conflictFiles.value = []
    selectedConflictFile.value = null
    await loadStatus()
  } catch (e) {
    Modal.error({ title: '完成合并失败', content: String(e) })
  } finally {
    commitLoading.value = false
  }
}

async function handleAbortMerge() {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认中止合并',
    icon: h(ExclamationCircleOutlined),
    content: '中止合并将丢弃所有合并更改，回到合并之前的状态。确定继续？',
    okText: '中止合并',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.AbortMerge(props.project.path)
        message.success('已中止合并')
        isMerging.value = false
        conflictFiles.value = []
        selectedConflictFile.value = null
        await loadStatus()
      } catch (e) {
        Modal.error({ title: '中止合并失败', content: String(e) })
      }
    },
  })
}

// ---- 提交搜索 ----
async function searchCommits() {
  if (!props.project?.path) return
  if (!searchKeyword.value.trim() && !searchAuthor.value.trim()) return
  searchLoading.value = true
  isSearchMode.value = true
  try {
    const results = await AppService.SearchCommitLog(
      props.project.path,
      searchKeyword.value.trim(),
      searchAuthor.value.trim(),
      100
    )
    searchResults.value = Array.isArray(results) ? results : []
  } catch (e) {
    console.error('搜索提交失败:', e)
    searchResults.value = []
  } finally {
    searchLoading.value = false
  }
}

function clearSearch() {
  isSearchMode.value = false
  searchKeyword.value = ''
  searchAuthor.value = ''
  searchResults.value = []
}

// ---- 提交历史 ----
async function loadCommitLog() {
  if (!props.project?.path) return
  commitLogsLoading.value = true
  try {
    const logs = await AppService.GetCommitLog(props.project.path, 100)
    commitLogs.value = Array.isArray(logs) ? logs : []
  } catch (e) {
    console.error('获取提交历史失败:', e)
  } finally {
    commitLogsLoading.value = false
  }
}

async function selectCommit(commit) {
  selectedCommit.value = commit
  selectedCommitFile.value = null
  commitFileDiff.value = ''
  commitDiff.value = ''
  commitFiles.value = []
  commitCollapsedDirs.value = new Set()
  commitFilesLoading.value = true
  try {
    const files = await AppService.GetCommitFiles(props.project.path, commit.hash)
    commitFiles.value = Array.isArray(files) ? files : []
  } catch (e) {
    console.error('获取提交文件列表失败:', e)
  } finally {
    commitFilesLoading.value = false
  }
}

async function selectCommitFile(file) {
  if (!selectedCommit.value) return
  selectedCommitFile.value = file
  commitFileDiff.value = ''
  viewMode.value = 'diff'
  try {
    const diff = await AppService.GetCommitFileDiff(props.project.path, selectedCommit.value.hash, file.filePath)
    commitFileDiff.value = diff || '(无差异)'
  } catch (e) {
    commitFileDiff.value = '获取 diff 失败: ' + e
  }
}

function toggleCommitDir(dirPath) {
  const s = new Set(commitCollapsedDirs.value)
  if (s.has(dirPath)) s.delete(dirPath)
  else s.add(dirPath)
  commitCollapsedDirs.value = s
}

// 为提交文件构建树
const commitFileTree = computed(() => {
  if (!commitFiles.value.length) return []
  // 复用 buildTree 的逻辑，但需要适配 CommitFileInfo 结构
  const files = commitFiles.value.map(f => ({ ...f, staged: false }))
  return buildTree(files)
})

function getCommitStatusLabel(status) {
  const map = { M: '修改', A: '新增', D: '删除', R: '重命名', C: '复制' }
  return map[status] || status
}
function getCommitStatusColor(status) {
  const map = { M: 'orange', A: 'green', D: 'red', R: 'blue', C: 'cyan' }
  return map[status] || 'default'
}

async function resetToCommit(commit, mode = 'hard') {
  if (!props.project?.path || !commit) return
  const modeLabel = { hard: '硬回滚（丢弃所有更改）', soft: '软回滚（保留到暂存区）', mixed: '混合回滚（保留到工作区）' }
  Modal.confirm({
    title: '确认版本回滚',
    icon: h(ExclamationCircleOutlined),
    content: h('div', [
      h('p', `确定要回滚到以下提交吗？`),
      h('p', { style: 'font-family: monospace; color: #89b4fa;' }, `${commit.shortHash} - ${commit.message}`),
      h('p', { style: 'color: #f38ba8; font-size: 12px; margin-top: 8px;' }, `模式：${modeLabel[mode] || modeLabel.hard}`),
      mode === 'hard' ? h('p', { style: 'color: #f38ba8; font-size: 12px; font-weight: bold;' }, '⚠ 此操作不可逆，所有未提交的更改将被丢弃！') : null,
    ]),
    okText: '确认回滚',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      resetLoading.value = true
      try {
        await AppService.ResetProject(props.project.path, commit.hash, mode)
        // 回滚成功后刷新所有状态
        await loadStatus()
        selectedCommit.value = null
        commitDiff.value = ''
      } catch (e) {
        console.error('版本回滚失败:', e)
        Modal.error({ title: '回滚失败', content: String(e) })
      } finally {
        resetLoading.value = false
      }
    },
  })
}

async function revertCommit(commit) {
  if (!props.project?.path || !commit) return
  Modal.confirm({
    title: '确认撤回提交',
    icon: h(ExclamationCircleOutlined),
    content: h('div', [
      h('p', '将创建一个新提交来撤回以下提交的更改：'),
      h('p', { style: 'font-family: monospace; color: #89b4fa;' }, `${commit.shortHash} - ${commit.message}`),
      h('p', { style: 'color: var(--text-muted); font-size: 12px; margin-top: 8px;' }, '此操作会生成一个新的反向提交，不会丢失历史记录。'),
    ]),
    okText: '确认撤回',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.RevertCommit(props.project.path, commit.hash)
        await loadStatus()
        selectedCommit.value = null
        commitDiff.value = ''
      } catch (e) {
        console.error('撤回提交失败:', e)
        Modal.error({ title: '撤回失败', content: String(e) })
      }
    },
  })
}

function formatTime(ts) {
  if (!ts) return ''
  const d = new Date(ts * 1000)
  const now = new Date()
  const diff = Math.floor((now - d) / 1000)
  if (diff < 60) return '刚刚'
  if (diff < 3600) return Math.floor(diff / 60) + ' 分钟前'
  if (diff < 86400) return Math.floor(diff / 3600) + ' 小时前'
  if (diff < 604800) return Math.floor(diff / 86400) + ' 天前'
  return d.toLocaleDateString('zh-CN')
}

// ---- 标签管理 ----
async function loadTags() {
  if (!props.project?.path) return
  tagsLoading.value = true
  try {
    const list = await AppService.GetTags(props.project.path)
    tags.value = Array.isArray(list) ? list : []
  } catch (e) {
    console.error('获取标签失败:', e)
    tags.value = []
  } finally {
    tagsLoading.value = false
  }
}

async function createTag() {
  if (!props.project?.path || !newTagName.value.trim()) return
  createTagLoading.value = true
  try {
    await AppService.CreateTag(props.project.path, newTagName.value.trim(), newTagMessage.value.trim() || newTagName.value.trim())
    newTagName.value = ''
    newTagMessage.value = ''
    showCreateTag.value = false
    await loadTags()
  } catch (e) {
    console.error('创建标签失败:', e)
    Modal.error({ title: '创建标签失败', content: String(e) })
  } finally {
    createTagLoading.value = false
  }
}

async function deleteTag(tag) {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认删除标签',
    icon: h(ExclamationCircleOutlined),
    content: h('div', [
      h('p', `确定要删除标签吗？`),
      h('p', { style: 'font-family: monospace; color: #89b4fa; font-size: 15px;' }, tag.name),
      h('p', { style: 'color: var(--text-muted); font-size: 12px; margin-top: 8px;' }, '将同时删除本地和远程标签。'),
    ]),
    okText: '确认删除',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.DeleteTag(props.project.path, tag.name, currentRemote.value)
        await loadTags()
      } catch (e) {
        console.error('删除标签失败:', e)
        Modal.error({ title: '删除失败', content: String(e) })
      }
    },
  })
}

async function pushTag(tag) {
  if (!props.project?.path) return
  try {
    await AppService.PushTag(props.project.path, tag.name, currentRemote.value)
    Modal.success({ title: '推送成功', content: `标签 ${tag.name} 已推送到远程` })
  } catch (e) {
    console.error('推送标签失败:', e)
    Modal.error({ title: '推送失败', content: String(e) })
  }
}

// 切换到标签 Tab 时加载标签
watch(activeTab, (tab) => {
  if (tab === 'tags' && !tags.value.length && !tagsLoading.value) {
    loadTags()
  }
  if (tab === 'releases' && !releases.value.length && releaseSyncSource.value) {
    loadReleases()
  }
})

// ---- Release 同步 ----
async function loadReleases() {
  if (!props.project?.path || !releaseSyncSource.value) return
  releasesLoading.value = true
  selectedRelease.value = null
  try {
    const list = await AppService.GetRemoteReleases(props.project.path, releaseSyncSource.value, releaseSrcToken.value)
    releases.value = Array.isArray(list) ? list : []
  } catch (e) {
    console.error('获取 Release 失败:', e)
    releases.value = []
    message.error('获取 Release 失败: ' + String(e))
  } finally {
    releasesLoading.value = false
  }
}

async function doSyncReleases() {
  if (!props.project?.path || !releaseSyncSource.value || !releaseSyncTarget.value) return
  releaseSyncLoading.value = true
  releaseSyncResults.value = []
  try {
    const results = await AppService.SyncReleases(props.project.path, releaseSyncSource.value, releaseSyncTarget.value, releaseSrcToken.value, releaseTgtToken.value)
    releaseSyncResults.value = Array.isArray(results) ? results : []
    const successCount = releaseSyncResults.value.filter(r => r.success).length
    message.info(`Release 同步完成：${successCount}/${releaseSyncResults.value.length}`)
  } catch (e) {
    console.error('Release 同步失败:', e)
    Modal.error({ title: 'Release 同步失败', content: String(e) })
  } finally {
    releaseSyncLoading.value = false
  }
}

function formatFileSize(bytes) {
  if (!bytes || bytes <= 0) return ''
  const units = ['B', 'KB', 'MB', 'GB']
  let i = 0
  let size = bytes
  while (size >= 1024 && i < units.length - 1) {
    size /= 1024
    i++
  }
  return size.toFixed(i === 0 ? 0 : 1) + ' ' + units[i]
}

// ---- Remote 远程仓库管理 ----
async function switchRemote(remoteName) {
  currentRemote.value = remoteName
  showRemoteDropdown.value = false
  // 切换 remote 后刷新远程分支
  await loadRemoteBranches()
}

async function addRemote() {
  if (!props.project?.path || !newRemoteName.value.trim() || !newRemoteUrl.value.trim()) return
  addRemoteLoading.value = true
  try {
    await AppService.AddRemote(props.project.path, newRemoteName.value.trim(), newRemoteUrl.value.trim())
    newRemoteName.value = ''
    newRemoteUrl.value = ''
    showAddRemote.value = false
    message.success('远程仓库添加成功')
    await loadStatus()
  } catch (e) {
    Modal.error({ title: '添加远程仓库失败', content: String(e) })
  } finally {
    addRemoteLoading.value = false
  }
}

async function removeRemote(remoteName) {
  if (!props.project?.path) return
  Modal.confirm({
    title: '确认删除远程仓库',
    icon: h(ExclamationCircleOutlined),
    content: h('div', [
      h('p', '确定要删除远程仓库吗？'),
      h('p', { style: 'font-family: monospace; color: #f38ba8; font-size: 15px;' }, remoteName),
    ]),
    okText: '删除',
    cancelText: '取消',
    okButtonProps: { danger: true },
    async onOk() {
      try {
        await AppService.RemoveRemote(props.project.path, remoteName)
        message.success(`远程仓库 ${remoteName} 已删除`)
        if (currentRemote.value === remoteName) {
          currentRemote.value = 'origin'
        }
        await loadStatus()
      } catch (e) {
        Modal.error({ title: '删除远程仓库失败', content: String(e) })
      }
    },
  })
}

const currentRemoteUrl = computed(() => {
  const r = remotes.value.find(r => r.name === currentRemote.value)
  return r ? r.url : status.value?.remoteUrl || ''
})
</script>

<template>
  <div class="content-area">
    <!-- 未选择项目 -->
    <div v-if="!project" class="empty-state">
      <FileOutlined :style="{ fontSize: '48px', color: 'var(--text-muted)' }" />
      <span style="color: var(--text-muted)">从左侧栏选择项目查看详情</span>
    </div>

    <!-- 项目内容 -->
    <div v-else class="project-view">
      <!-- 渲染错误提示 -->
      <div v-if="renderError" style="background: #f5222d; color: #fff; padding: 8px 16px; font-size: 13px;">
        渲染错误: {{ renderError }}
      </div>

      <!-- 错误状态 -->
      <div v-if="errorMsg" style="padding: 24px; text-align: center; color: var(--text-muted);">
        <div style="font-size: 36px; margin-bottom: 12px;">⚠️</div>
        <div>{{ errorMsg }}</div>
        <a-button style="margin-top: 12px;" @click="loadStatus">重试</a-button>
      </div>

      <!-- 正常内容 -->
      <template v-else>
        <!-- 顶部信息栏 -->
        <div class="project-header">
          <div class="project-info">
            <span class="project-name">{{ project.label }}</span>
            <template v-if="loadingBase">
              <a-spin size="small" />
            </template>
            <template v-else-if="status">
              <a-dropdown :open="showBranchDropdown" @openChange="v => showBranchDropdown = v" :trigger="['click']">
                <a-tag color="green" class="branch-tag" @click.prevent="showBranchDropdown = !showBranchDropdown">
                  <BranchesOutlined /> {{ status.branch }}
                  <CaretDownOutlined style="font-size: 10px; margin-left: 2px;" />
                </a-tag>
                <template #overlay>
                  <div class="branch-dropdown">
                    <!-- 创建新分支 -->
                    <div class="branch-create-box">
                      <a-input
                        v-model:value="newBranchName"
                        placeholder="新分支名称"
                        size="small"
                        style="flex:1"
                        @pressEnter="createBranch"
                      />
                      <a-button
                        type="primary"
                        size="small"
                        :loading="createBranchLoading"
                        :disabled="!newBranchName.trim()"
                        @click="createBranch"
                      >
                        <template #icon><PlusOutlined /></template>
                      </a-button>
                    </div>
                    <!-- 本地分支 -->
                    <div class="branch-dropdown-title">本地分支</div>
                    <div class="branch-dropdown-list">
                      <div
                        v-for="b in branches"
                        :key="b.name"
                        class="branch-dropdown-item"
                        :class="{ active: b.current }"
                      >
                        <div class="branch-item-main" @click="switchBranch(b.name)">
                          <BranchesOutlined style="font-size: 12px; margin-right: 6px;" />
                          <span class="branch-item-name">{{ b.name }}</span>
                          <span v-if="b.current" style="margin-left: auto; color: var(--success, #a6e3a1);">✓</span>
                        </div>
                        <div v-if="!b.current" class="branch-item-actions" @click.stop>
                          <a-tooltip title="合并到当前分支">
                            <span class="branch-action-btn" @click="mergeBranch(b.name)"><MergeCellsOutlined /></span>
                          </a-tooltip>
                          <a-tooltip title="删除分支">
                            <span class="branch-action-btn danger" @click="deleteBranch(b.name)"><DeleteOutlined /></span>
                          </a-tooltip>
                        </div>
                      </div>
                      <div v-if="!branches.length" style="padding: 12px; text-align: center; color: var(--text-muted);">
                        无分支数据
                      </div>
                    </div>
                    <!-- 远程分支 -->
                    <template v-if="remoteBranches.length">
                      <div class="branch-dropdown-title">远程分支</div>
                      <div class="branch-dropdown-list" style="max-height: 150px;">
                        <div
                          v-for="rb in remoteBranches"
                          :key="rb.name"
                          class="branch-dropdown-item remote"
                        >
                          <div class="branch-item-main" @click="checkoutRemoteBranch(rb.name)">
                            <GlobalOutlined style="font-size: 12px; margin-right: 6px; color: var(--text-muted);" />
                            <span class="branch-item-name">{{ rb.name }}</span>
                          </div>
                          <div class="branch-item-actions" @click.stop>
                            <a-tooltip title="检出到本地">
                              <span class="branch-action-btn" @click="checkoutRemoteBranch(rb.name)"><DownloadOutlined /></span>
                            </a-tooltip>
                            <a-tooltip title="删除远程分支">
                              <span class="branch-action-btn danger" @click="deleteRemoteBranch(rb.name)"><DeleteOutlined /></span>
                            </a-tooltip>
                          </div>
                        </div>
                      </div>
                    </template>
                  </div>
                </template>
              </a-dropdown>
              <span v-if="status.remoteUrl" class="remote-url" :title="currentRemoteUrl">
                <a-dropdown :open="showRemoteDropdown" @openChange="v => showRemoteDropdown = v" :trigger="['click']">
                  <span class="remote-selector" @click.prevent="showRemoteDropdown = !showRemoteDropdown">
                    <GlobalOutlined style="margin-right: 4px;" />
                    {{ currentRemote }}
                    <CaretDownOutlined style="font-size: 10px; margin-left: 2px;" />
                  </span>
                  <template #overlay>
                    <div class="remote-dropdown">
                      <div class="branch-dropdown-title">远程仓库</div>
                      <div class="branch-dropdown-list">
                        <div
                          v-for="r in remotes"
                          :key="r.name"
                          class="branch-dropdown-item"
                          :class="{ active: r.name === currentRemote }"
                        >
                          <div class="branch-item-main" @click="switchRemote(r.name)">
                            <GlobalOutlined style="font-size: 12px; margin-right: 6px;" />
                            <span class="branch-item-name">{{ r.name }}</span>
                            <span v-if="r.name === currentRemote" style="margin-left: auto; color: var(--success, #a6e3a1);">✓</span>
                          </div>
                          <div class="branch-item-actions" @click.stop>
                            <a-tooltip :title="r.url">
                              <span class="branch-action-btn" style="cursor: default; opacity: 0.6; font-size: 10px;">URL</span>
                            </a-tooltip>
                            <a-tooltip title="删除远程仓库" v-if="r.name !== 'origin'">
                              <span class="branch-action-btn danger" @click="removeRemote(r.name)"><DeleteOutlined /></span>
                            </a-tooltip>
                          </div>
                        </div>
                        <div v-if="!remotes.length" style="padding: 12px; text-align: center; color: var(--text-muted);">
                          无远程仓库
                        </div>
                      </div>
                      <!-- 添加远程仓库 -->
                      <div v-if="!showAddRemote" class="remote-add-btn" @click="showAddRemote = true">
                        <PlusOutlined /> 添加远程仓库
                      </div>
                      <div v-else class="remote-add-form">
                        <a-input v-model:value="newRemoteName" placeholder="名称 (如 upstream)" size="small" style="margin-bottom: 4px;" />
                        <a-input v-model:value="newRemoteUrl" placeholder="仓库地址" size="small" style="margin-bottom: 4px;" />
                        <div style="display: flex; gap: 4px;">
                          <a-button size="small" type="primary" :loading="addRemoteLoading" :disabled="!newRemoteName.trim() || !newRemoteUrl.trim()" @click="addRemote" style="flex: 1;">添加</a-button>
                          <a-button size="small" @click="showAddRemote = false; newRemoteName = ''; newRemoteUrl = ''">取消</a-button>
                        </div>
                      </div>
                    </div>
                  </template>
                </a-dropdown>
                <span class="remote-url-text" :title="currentRemoteUrl">{{ currentRemoteUrl }}</span>
              </span>
            </template>
          </div>
          <a-space class="project-actions">
            <a-tooltip :title="projectProxy === null ? '代理: 跟随全局' : projectProxy ? '代理: 已开启' : '代理: 已关闭'">
              <a-dropdown :trigger="['click']">
                <a-button size="small" :type="projectProxy === true ? 'primary' : 'default'" :ghost="projectProxy === true">
                  <template #icon><GlobalOutlined /></template>
                </a-button>
                <template #overlay>
                  <div class="proxy-dropdown">
                    <div class="proxy-dropdown-title">代理设置</div>
                    <div class="proxy-dropdown-item" :class="{ active: projectProxy === null }" @click="resetProxy()">
                      <span>跟随全局</span>
                      <span v-if="projectProxy === null" style="color: var(--success, #a6e3a1);">✓</span>
                    </div>
                    <div class="proxy-dropdown-item" :class="{ active: projectProxy === true }" @click="toggleProxy(true)">
                      <span>强制使用代理</span>
                      <span v-if="projectProxy === true" style="color: var(--success, #a6e3a1);">✓</span>
                    </div>
                    <div class="proxy-dropdown-item" :class="{ active: projectProxy === false }" @click="toggleProxy(false)">
                      <span>强制不使用代理</span>
                      <span v-if="projectProxy === false" style="color: var(--success, #a6e3a1);">✓</span>
                    </div>
                  </div>
                </template>
              </a-dropdown>
            </a-tooltip>
            <a-button size="small" :loading="actionLoading === 'fetch'" :disabled="loadingBase" @click="gitAction('fetch')">
              <template #icon><SyncOutlined /></template>
              Fetch
            </a-button>
            <a-button size="small" :loading="actionLoading === 'pull'" :disabled="loadingBase" @click="gitAction('pull')">
              <template #icon><CloudDownloadOutlined /></template>
              Pull
            </a-button>
            <a-button size="small" :loading="actionLoading === 'push'" :disabled="loadingBase" @click="gitAction('push')">
              <template #icon><CloudUploadOutlined /></template>
              Push
            </a-button>
            <a-tooltip title="推送到所有远程仓库">
              <a-button size="small" :loading="actionLoading === 'pushAll'" :disabled="loadingBase || remotes.length < 2" @click="pushToAllRemotes">
                <template #icon><SendOutlined /></template>
                Push All
              </a-button>
            </a-tooltip>
            <a-button size="small" :disabled="loadingBase" @click="loadStatus">
              <template #icon><ReloadOutlined /></template>
            </a-button>
            <a-button size="small" @click="openSettings">
              <template #icon><SettingOutlined /></template>
            </a-button>
          </a-space>
        </div>

        <div class="project-body">
          <!-- 文件列表 -->
          <div class="file-list" :style="{ width: fileListWidth + 'px' }">
            <!-- Tab 切换：变更 / 历史 -->
            <div class="file-list-tabs">
              <div class="file-list-tab" :class="{ active: activeTab === 'changes' }" @click="activeTab = 'changes'">
                <EditOutlined /> 变更
                <a-badge v-if="changedFiles.length" :count="changedFiles.length" :number-style="{ backgroundColor: 'var(--accent)', fontSize: '10px', height: '16px', lineHeight: '16px', minWidth: '16px', padding: '0 4px' }" />
              </div>
              <div class="file-list-tab" :class="{ active: activeTab === 'history' }" @click="activeTab = 'history'">
                <HistoryOutlined /> 历史
              </div>
              <div class="file-list-tab" :class="{ active: activeTab === 'tags' }" @click="activeTab = 'tags'">
                <TagsOutlined /> 标签
              </div>
              <div class="file-list-tab" :class="{ active: activeTab === 'releases' }" @click="activeTab = 'releases'">
                <DownloadOutlined /> 发布
              </div>
            </div>

            <!-- ===== 变更面板 ===== -->
            <template v-if="activeTab === 'changes'">
              <!-- 提交输入区 -->
              <div class="commit-box">
              <div class="commit-input-row">
                <a-input
                  v-model:value="commitMessage"
                  placeholder="提交信息"
                  size="small"
                  :disabled="!stagedFiles.length"
                  @pressEnter="commitChanges"
                />
                <a-button
                  type="primary"
                  size="small"
                  :loading="commitLoading"
                  :disabled="!commitMessage.trim() || !stagedFiles.length"
                  @click="commitChanges"
                  title="提交暂存的更改"
                >
                  Commit
                </a-button>
                <a-dropdown :trigger="['click']">
                  <a-button size="small" :disabled="!unstagedFiles.length && !stagedFiles.length" title="贮藏">
                    <template #icon><InboxOutlined /></template>
                  </a-button>
                  <template #overlay>
                    <div class="stash-dropdown">
                      <div class="stash-save-section">
                        <a-input
                          v-model:value="stashMessage"
                          placeholder="贮藏描述 (可选)"
                          size="small"
                          style="flex:1"
                          @pressEnter="stashSave"
                        />
                        <a-button type="primary" size="small" :loading="stashSaveLoading" @click="stashSave">
                          <template #icon><SaveOutlined /></template>
                          Stash
                        </a-button>
                      </div>
                      <div class="stash-list-title" v-if="stashList.length">
                        <InboxOutlined /> 贮藏列表 ({{ stashList.length }})
                      </div>
                      <div class="stash-list" v-if="stashList.length">
                        <div v-for="s in stashList" :key="s.index" class="stash-item">
                          <div class="stash-item-main">
                            <span class="stash-ref">{{ s.ref }}</span>
                            <span class="stash-msg">{{ s.message }}</span>
                          </div>
                          <div class="stash-item-actions">
                            <a-tooltip title="应用并删除">
                              <span class="stash-action-btn" @click="stashPop(s.index)"><CheckCircleOutlined /></span>
                            </a-tooltip>
                            <a-tooltip title="应用（保留）">
                              <span class="stash-action-btn" @click="stashApply(s.index)"><DownloadOutlined /></span>
                            </a-tooltip>
                            <a-tooltip title="删除">
                              <span class="stash-action-btn danger" @click="stashDrop(s.index)"><DeleteOutlined /></span>
                            </a-tooltip>
                          </div>
                        </div>
                      </div>
                      <div v-else style="padding: 12px; text-align: center; color: var(--text-muted); font-size: 12px;">
                        暂无贮藏
                      </div>
                    </div>
                  </template>
                </a-dropdown>
              </div>
            </div>

            <!-- 合并冲突提示栏 -->
            <div v-if="isMerging" class="conflict-banner">
              <div class="conflict-banner-header">
                <WarningOutlined style="color: #fab387;" />
                <span style="font-weight: 600;">合并冲突</span>
                <span v-if="conflictFiles.length" style="color: var(--text-muted); font-size: 12px;">
                  {{ conflictFiles.length }} 个文件需要解决
                </span>
                <span style="flex:1"></span>
                <a-space :size="4">
                  <a-button size="small" danger @click="handleAbortMerge">
                    <template #icon><StopOutlined /></template>
                    中止
                  </a-button>
                  <a-button
                    size="small"
                    type="primary"
                    :disabled="conflictFiles.length > 0"
                    :loading="commitLoading"
                    @click="completeMerge"
                  >
                    完成合并
                  </a-button>
                </a-space>
              </div>
              <div v-if="conflictFiles.length" class="conflict-file-list">
                <div
                  v-for="cf in conflictFiles"
                  :key="cf.filePath"
                  class="conflict-file-item"
                  :class="{ active: selectedConflictFile?.filePath === cf.filePath }"
                  @click="selectConflictFile(cf)"
                >
                  <WarningOutlined style="color: #fab387; font-size: 12px;" />
                  <span class="conflict-file-name">{{ cf.filePath }}</span>
                  <div class="conflict-file-actions" @click.stop>
                    <a-tooltip title="标记为已解决">
                      <span class="conflict-resolve-btn" @click="resolveConflictFiles([cf])">
                        <CheckCircleOutlined />
                      </span>
                    </a-tooltip>
                  </div>
                </div>
              </div>
              <div v-if="conflictFiles.length > 1" style="padding: 4px 8px; text-align: right; border-top: 1px solid var(--border-color);">
                <a-button size="small" type="link" @click="resolveConflictFiles(conflictFiles)">
                  <CheckCircleOutlined /> 全部标记为已解决
                </a-button>
              </div>
            </div>

            <div class="file-list-content">
              <!-- 骨架屏 -->
              <template v-if="loadingBase || loadingFiles">
                <div v-for="i in 6" :key="i" style="padding: 10px 12px;">
                  <div style="height: 14px; background: var(--bg-hover, #333); border-radius: 4px; animation: pulse 1.5s infinite;" :style="{ width: (50 + i * 8) + '%' }"></div>
                </div>
              </template>
              <!-- 无变更 -->
              <div v-else-if="!changedFiles.length" style="padding: 24px; text-align: center; color: var(--text-muted);">
                <CheckCircleOutlined /> 工作区干净，无变更文件
              </div>
              <template v-else>
                <!-- ===== 已暂存的更改 ===== -->
                <div v-if="stagedFiles.length" class="section-group">
                  <div class="section-header" @click="stagedCollapsed = !stagedCollapsed">
                    <CaretDownOutlined v-if="!stagedCollapsed" class="section-arrow" />
                    <CaretRightOutlined v-else class="section-arrow" />
                    <span class="section-title">已暂存的更改</span>
                    <a-badge :count="stagedFiles.length" :number-style="{ backgroundColor: 'var(--success, #a6e3a1)', color: '#1e1e2e' }" />
                    <span style="flex:1"></span>
                    <span class="section-actions" @click.stop>
                      <span class="section-action-btn" title="取消暂存全部" @click="unstageAll"><MinusOutlined /></span>
                    </span>
                  </div>
                  <div v-show="!stagedCollapsed">
                    <FileTreeNode
                      v-for="node in stagedTree"
                      :key="'staged-' + node.type + '-' + (node.path || node.data?.filePath)"
                      :node="node"
                      :collapsed-dirs="collapsedDirs"
                      :selected-file="selectedFile"
                      :depth="0"
                      mode="staged"
                      @select-file="selectFile"
                      @toggle-dir="toggleDir"
                      @unstage-file="unstageFile"
                      @unstage-dir="unstageDir"
                    />
                  </div>
                </div>

                <!-- ===== 更改 (未暂存) ===== -->
                <div v-if="unstagedFiles.length" class="section-group">
                  <div class="section-header" @click="unstagedCollapsed = !unstagedCollapsed">
                    <CaretDownOutlined v-if="!unstagedCollapsed" class="section-arrow" />
                    <CaretRightOutlined v-else class="section-arrow" />
                    <span class="section-title">更改</span>
                    <a-badge :count="unstagedFiles.length" :number-style="{ backgroundColor: 'var(--accent, #89b4fa)', color: '#1e1e2e' }" />
                    <span style="flex:1"></span>
                    <span class="section-actions" @click.stop>
                      <span class="section-action-btn" title="暂存全部" @click="stageAll"><PlusOutlined /></span>
                      <span class="section-action-btn danger" title="丢弃全部更改" @click="discardAll"><UndoOutlined /></span>
                    </span>
                  </div>
                  <div v-show="!unstagedCollapsed">
                    <FileTreeNode
                      v-for="node in unstagedTree"
                      :key="'unstaged-' + node.type + '-' + (node.path || node.data?.filePath)"
                      :node="node"
                      :collapsed-dirs="collapsedDirs"
                      :selected-file="selectedFile"
                      :depth="0"
                      mode="unstaged"
                      @select-file="selectFile"
                      @toggle-dir="toggleDir"
                      @stage-file="stageFile"
                      @stage-dir="stageDir"
                      @discard-file="discardFile"
                      @discard-dir="discardDir"
                    />
                  </div>
                </div>
              </template>
            </div>
            </template>

            <!-- ===== 提交历史面板 ===== -->
            <template v-else-if="activeTab === 'history'">
              <div class="file-list-content history-panel">
                <!-- 搜索栏 -->
                <div class="search-bar">
                  <a-input
                    v-model:value="searchKeyword"
                    placeholder="搜索提交信息..."
                    size="small"
                    allow-clear
                    @pressEnter="searchCommits"
                  >
                    <template #prefix><SearchOutlined style="color: var(--text-muted);" /></template>
                  </a-input>
                  <a-input
                    v-model:value="searchAuthor"
                    placeholder="作者"
                    size="small"
                    style="width: 100px;"
                    allow-clear
                    @pressEnter="searchCommits"
                  >
                    <template #prefix><UserOutlined style="color: var(--text-muted);" /></template>
                  </a-input>
                  <a-button size="small" type="primary" :loading="searchLoading" @click="searchCommits">
                    <template #icon><SearchOutlined /></template>
                  </a-button>
                  <a-button v-if="isSearchMode" size="small" @click="clearSearch">清除</a-button>
                </div>
                <!-- 提交列表区域 -->
                <div class="commit-list-section" :class="{ 'has-selected': selectedCommit }">
                  <template v-if="commitLogsLoading || searchLoading">
                    <div v-for="i in 8" :key="i" style="padding: 10px 12px;">
                      <div style="height: 14px; background: var(--bg-hover, #333); border-radius: 4px; animation: pulse 1.5s infinite;" :style="{ width: (40 + i * 7) + '%' }"></div>
                      <div style="height: 10px; background: var(--bg-hover, #333); border-radius: 4px; animation: pulse 1.5s infinite; margin-top: 4px; width: 40%;"></div>
                    </div>
                  </template>
                  <div v-else-if="!displayLogs.length" style="padding: 24px; text-align: center; color: var(--text-muted);">
                    {{ isSearchMode ? '未找到匹配的提交' : '暂无提交历史' }}
                  </div>
                  <template v-else>
                    <div
                      v-for="log in displayLogs"
                      :key="log.hash"
                      class="commit-item"
                      :class="{ active: selectedCommit?.hash === log.hash, unpushed: !log.pushed }"
                      @click="selectCommit(log)"
                    >
                      <div class="commit-msg-row">
                        <span v-if="!log.pushed" class="commit-unpushed-dot" title="未推送"></span>
                        <div class="commit-msg">{{ log.message }}</div>
                        <div class="commit-action-btns" @click.stop>
                          <!-- 撤回按钮 -->
                          <a-button
                            type="text"
                            size="small"
                            class="commit-action-btn"
                            title="撤回此提交"
                            @click.stop="revertCommit(log)"
                          >
                            <template #icon><CloseCircleOutlined /></template>
                          </a-button>
                          <!-- 回滚下拉 -->
                          <a-dropdown :trigger="['click']">
                            <a-button
                              type="text"
                              size="small"
                              class="commit-action-btn"
                              :loading="resetLoading"
                              @click.stop
                            >
                              <template #icon><RollbackOutlined /></template>
                            </a-button>
                            <template #overlay>
                              <a-menu @click="({ key }) => resetToCommit(log, key)">
                                <a-menu-item key="hard">
                                  <span style="color: #f38ba8;">🔴 硬回滚</span>
                                  <span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">丢弃所有更改</span>
                                </a-menu-item>
                                <a-menu-item key="mixed">
                                  <span style="color: #fab387;">🟡 混合回滚</span>
                                  <span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">保留到工作区</span>
                                </a-menu-item>
                                <a-menu-item key="soft">
                                  <span style="color: #a6e3a1;">🟢 软回滚</span>
                                  <span style="font-size: 11px; color: var(--text-muted); margin-left: 8px;">保留到暂存区</span>
                                </a-menu-item>
                              </a-menu>
                            </template>
                          </a-dropdown>
                        </div>
                      </div>
                      <div class="commit-meta">
                        <span class="commit-hash">{{ log.shortHash }}</span>
                        <span v-if="!log.pushed" class="commit-push-tag">未推送</span>
                        <span class="commit-author"><UserOutlined /> {{ log.author }}</span>
                        <span class="commit-time">{{ formatTime(log.timestamp) }}</span>
                      </div>
                    </div>
                  </template>
                </div>

                <!-- 选中提交的变更文件列表 -->
                <template v-if="selectedCommit">
                  <div class="commit-files-divider">
                    <span class="commit-files-title">
                      <EditOutlined /> {{ selectedCommit.shortHash }} 变更文件
                      <a-badge v-if="commitFiles.length" :count="commitFiles.length" :number-style="{ backgroundColor: 'var(--accent, #89b4fa)', color: '#1e1e2e', marginLeft: '6px' }" />
                    </span>
                  </div>
                  <div class="commit-files-section">
                    <template v-if="commitFilesLoading">
                      <div v-for="i in 4" :key="i" style="padding: 6px 12px;">
                        <div style="height: 14px; background: var(--bg-hover, #333); border-radius: 4px; animation: pulse 1.5s infinite;" :style="{ width: (30 + i * 12) + '%' }"></div>
                      </div>
                    </template>
                    <div v-else-if="!commitFiles.length" style="padding: 12px; text-align: center; color: var(--text-muted); font-size: 12px;">
                      无变更文件
                    </div>
                    <template v-else>
                      <CommitFileTreeNode
                        v-for="cf in commitFileTree"
                        :key="cf.type + '-' + (cf.path || cf.data?.filePath)"
                        :node="cf"
                        :collapsed-dirs="commitCollapsedDirs"
                        :selected-file="selectedCommitFile"
                        @select-file="selectCommitFile($event)"
                        @toggle-dir="toggleCommitDir($event)"
                      />
                    </template>
                  </div>
                </template>
              </div>
            </template>

            <!-- ===== 标签管理面板 ===== -->
            <template v-else-if="activeTab === 'tags'">
              <div class="file-list-content tags-panel">
                <!-- 创建标签区域 -->
                <div class="tag-create-box">
                  <div v-if="!showCreateTag" style="display: flex; justify-content: flex-end; padding: 6px 0;">
                    <a-button size="small" type="primary" @click="showCreateTag = true">
                      <template #icon><PlusOutlined /></template>
                      新建标签
                    </a-button>
                  </div>
                  <template v-else>
                    <a-input v-model:value="newTagName" placeholder="标签名 (例: v1.0.0)" size="small" style="margin-bottom: 6px;" @pressEnter="createTag" />
                    <a-input v-model:value="newTagMessage" placeholder="标签描述 (可选)" size="small" style="margin-bottom: 6px;" />
                    <div style="display: flex; gap: 6px; justify-content: flex-end;">
                      <a-button size="small" @click="showCreateTag = false; newTagName = ''; newTagMessage = ''">取消</a-button>
                      <a-button size="small" type="primary" :loading="createTagLoading" :disabled="!newTagName.trim()" @click="createTag">创建</a-button>
                    </div>
                  </template>
                </div>

                <!-- 标签列表 -->
                <div class="tag-list-section">
                  <template v-if="tagsLoading">
                    <div v-for="i in 5" :key="i" style="padding: 10px 12px;">
                      <div style="height: 14px; background: var(--bg-hover, #333); border-radius: 4px; animation: pulse 1.5s infinite;" :style="{ width: (40 + i * 8) + '%' }"></div>
                    </div>
                  </template>
                  <div v-else-if="!tags.length" style="padding: 24px; text-align: center; color: var(--text-muted);">
                    <TagOutlined :style="{ fontSize: '28px', marginBottom: '8px' }" />
                    <div>暂无标签</div>
                  </div>
                  <template v-else>
                    <div
                      v-for="tag in tags"
                      :key="tag.name"
                      class="tag-item"
                    >
                      <div class="tag-main-row">
                        <TagOutlined class="tag-icon" />
                        <span class="tag-name">{{ tag.name }}</span>
                        <div class="tag-action-btns" @click.stop>
                          <a-tooltip title="推送到当前远程">
                            <a-button type="text" size="small" class="tag-action-btn push" @click="pushTag(tag)">
                              <template #icon><SendOutlined /></template>
                            </a-button>
                          </a-tooltip>
                          <a-tooltip title="推送到所有远程" v-if="remotes.length > 1">
                            <a-button type="text" size="small" class="tag-action-btn push" @click="pushTagToAllRemotes(tag)">
                              <template #icon><CloudUploadOutlined /></template>
                            </a-button>
                          </a-tooltip>
                          <a-tooltip title="删除标签">
                            <a-button type="text" size="small" class="tag-action-btn delete" @click="deleteTag(tag)">
                              <template #icon><DeleteOutlined /></template>
                            </a-button>
                          </a-tooltip>
                        </div>
                      </div>
                      <div class="tag-meta">
                        <span class="tag-hash">{{ tag.hash }}</span>
                        <span v-if="tag.message" class="tag-message">{{ tag.message }}</span>
                        <span class="tag-time">{{ formatTime(tag.timestamp) }}</span>
                      </div>
                    </div>
                  </template>
                </div>

                <!-- 刷新按钮 -->
                <div style="padding: 8px 10px; border-top: 1px solid var(--border-color); text-align: center;">
                  <a-button size="small" :loading="tagsLoading" @click="loadTags" block>
                    <template #icon><ReloadOutlined /></template>
                    刷新标签
                  </a-button>
                </div>
              </div>
            </template>

            <!-- ===== 发布同步面板 ===== -->
            <template v-else-if="activeTab === 'releases'">
              <div class="file-list-content tags-panel">
                <!-- 源/目标 remote 选择 -->
                <div style="padding: 8px 10px; border-bottom: 1px solid var(--border-color);">
                  <div style="display: flex; gap: 6px; align-items: center; margin-bottom: 6px;">
                    <span style="font-size: 11px; color: var(--text-muted); width: 30px; flex-shrink: 0;">源</span>
                    <a-select v-model:value="releaseSyncSource" size="small" style="flex: 1;" placeholder="源 remote">
                      <a-select-option v-for="r in remotes" :key="r.name" :value="r.name">{{ r.name }}</a-select-option>
                    </a-select>
                    <a-button size="small" :loading="releasesLoading" @click="loadReleases" :disabled="!releaseSyncSource">
                      <template #icon><ReloadOutlined /></template>
                    </a-button>
                  </div>
                  <div style="display: flex; gap: 6px; align-items: center; margin-bottom: 6px;">
                    <span style="font-size: 11px; color: var(--text-muted); width: 30px; flex-shrink: 0;">目标</span>
                    <a-select v-model:value="releaseSyncTarget" size="small" style="flex: 1;" placeholder="目标 remote">
                      <a-select-option v-for="r in remotes" :key="r.name" :value="r.name" :disabled="r.name === releaseSyncSource">{{ r.name }}</a-select-option>
                    </a-select>
                    <a-button
                      size="small"
                      type="primary"
                      :loading="releaseSyncLoading"
                      :disabled="!releaseSyncSource || !releaseSyncTarget || releaseSyncSource === releaseSyncTarget"
                      @click="doSyncReleases"
                    >
                      <template #icon><SyncOutlined /></template>
                      同步
                    </a-button>
                  </div>
                  <div style="display: flex; gap: 6px; align-items: center; margin-bottom: 6px;">
                    <span style="font-size: 11px; color: var(--text-muted); width: 30px; flex-shrink: 0;">认证</span>
                    <a-input-password v-model:value="releaseSrcToken" size="small" style="flex: 1;" placeholder="源 Token（可选）" />
                    <a-input-password v-model:value="releaseTgtToken" size="small" style="flex: 1;" placeholder="目标 Token（可选）" />
                  </div>
                </div>

                <!-- Release 列表 -->
                <div class="tag-list-section">
                  <template v-if="releasesLoading">
                    <div v-for="i in 4" :key="i" style="padding: 10px 12px;">
                      <div style="height: 14px; background: var(--bg-hover, #333); border-radius: 4px; animation: pulse 1.5s infinite;" :style="{ width: (40 + i * 8) + '%' }"></div>
                    </div>
                  </template>
                  <div v-else-if="!releases.length && releaseSyncSource" style="padding: 24px; text-align: center; color: var(--text-muted);">
                    <DownloadOutlined :style="{ fontSize: '28px', marginBottom: '8px' }" />
                    <div>暂无 Release</div>
                  </div>
                  <div v-else-if="!releaseSyncSource" style="padding: 24px; text-align: center; color: var(--text-muted);">
                    <DownloadOutlined :style="{ fontSize: '28px', marginBottom: '8px' }" />
                    <div>选择源 remote 以查看 Release</div>
                  </div>
                  <template v-else>
                    <div
                      v-for="rel in releases"
                      :key="rel.tagName"
                      class="tag-item"
                      :class="{ active: selectedRelease?.tagName === rel.tagName }"
                      @click="selectedRelease = rel"
                    >
                      <div class="tag-main-row">
                        <TagOutlined class="tag-icon" />
                        <span class="tag-name">{{ rel.name || rel.tagName }}</span>
                        <a-tag v-if="rel.draft" size="small" color="orange">草稿</a-tag>
                        <a-tag v-if="rel.prerelease" size="small" color="blue">预发布</a-tag>
                        <a-badge v-if="rel.assets?.length" :count="rel.assets.length" :number-style="{ backgroundColor: 'var(--accent)', fontSize: '10px', height: '16px', lineHeight: '16px', minWidth: '16px', padding: '0 4px' }" />
                      </div>
                      <div class="tag-meta">
                        <span class="tag-hash">{{ rel.tagName }}</span>
                        <span v-if="rel.publishedAt || rel.createdAt" class="tag-time">{{ formatTime(rel.publishedAt || rel.createdAt) }}</span>
                      </div>
                    </div>
                  </template>
                </div>

                <!-- 同步结果 -->
                <div v-if="releaseSyncResults.length" style="padding: 8px 10px; border-top: 1px solid var(--border-color);">
                  <div style="font-size: 11px; color: var(--text-muted); margin-bottom: 4px;">同步结果</div>
                  <div v-for="r in releaseSyncResults" :key="r.tagName" style="display: flex; align-items: center; gap: 4px; padding: 2px 0; font-size: 11px;">
                    <CheckCircleOutlined v-if="r.success" style="color: #a6e3a1; flex-shrink: 0;" />
                    <CloseCircleOutlined v-else style="color: #f38ba8; flex-shrink: 0;" />
                    <span style="font-weight: 500;">{{ r.tagName }}</span>
                    <span style="color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ r.message }}</span>
                  </div>
                </div>
              </div>
            </template>
          </div>

          <!-- 拖拽分隔条 -->
          <div class="resize-handle-inner" @mousedown="startFileListResize"></div>

          <!-- 文件内容/Diff 展示 -->
          <div class="file-viewer">
            <!-- 变更模式的文件查看器 -->
            <template v-if="activeTab === 'changes'">
              <!-- 冲突文件编辑器 -->
              <template v-if="selectedConflictFile">
                <div class="viewer-header">
                  <WarningOutlined style="color: #fab387; margin-right: 6px;" />
                  <span class="viewer-path">{{ selectedConflictFile.filePath }}</span>
                  <a-tag color="warning" size="small" style="margin-left: 8px;">冲突</a-tag>
                  <span style="flex:1"></span>
                  <a-space :size="6">
                    <a-button size="small" :loading="conflictSaving" @click="saveConflictContent">
                      <template #icon><SaveOutlined /></template>
                      保存
                    </a-button>
                    <a-button size="small" type="primary" @click="resolveConflictFiles([selectedConflictFile])">
                      <template #icon><CheckCircleOutlined /></template>
                      标记已解决
                    </a-button>
                  </a-space>
                </div>
                <div class="viewer-content conflict-editor">
                  <textarea
                    v-model="conflictContent"
                    class="conflict-textarea"
                    spellcheck="false"
                  ></textarea>
                </div>
              </template>
              <!-- 普通文件查看器 -->
              <div v-else-if="!selectedFile" class="empty-state small">
                <span style="color: var(--text-muted)">点击左侧文件查看详情</span>
              </div>
              <template v-else>
                <div class="viewer-header">
                  <span class="viewer-path">{{ selectedFile.filePath }}</span>
                  <a-tag v-if="isNewFile(selectedFile)" color="green" size="small" style="margin-left: 8px;">新增文件</a-tag>
                  <span style="flex:1"></span>
                  <a-segmented v-model:value="viewMode" size="small" :options="[
                    { value: 'diff', label: 'Diff' },
                    { value: 'content', label: '内容' }
                  ]" />
                </div>
                <div class="viewer-content">
                  <pre v-if="viewMode === 'diff'" class="code-block diff-block"><code v-html="formatDiff(fileDiff)"></code></pre>
                  <pre v-else class="code-block"><code>{{ fileContent }}</code></pre>
                </div>
              </template>
            </template>
            <!-- 历史模式的文件 Diff -->
            <template v-else-if="activeTab === 'history'">
              <div v-if="!selectedCommit" class="empty-state small">
                <span style="color: var(--text-muted)">点击左侧提交查看详情</span>
              </div>
              <div v-else-if="!selectedCommitFile" class="empty-state small">
                <div style="text-align: center;">
                  <HistoryOutlined :style="{ fontSize: '32px', color: 'var(--text-muted)', marginBottom: '8px' }" />
                  <div style="color: var(--text-muted); font-size: 13px;">{{ selectedCommit.shortHash }} · {{ selectedCommit.message }}</div>
                  <div style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">{{ selectedCommit.author }} · {{ formatTime(selectedCommit.timestamp) }}</div>
                  <div style="color: var(--text-muted); font-size: 12px; margin-top: 12px;">点击左侧文件查看变更详情</div>
                </div>
              </div>
              <template v-else>
                <div class="viewer-header">
                  <span class="viewer-path">{{ selectedCommitFile.filePath }}</span>
                  <a-tag :color="getCommitStatusColor(selectedCommitFile.status)" size="small" style="margin-left: 8px;">{{ getCommitStatusLabel(selectedCommitFile.status) }}</a-tag>
                  <span style="flex:1"></span>
                  <span style="font-size: 12px; color: var(--text-muted);">{{ selectedCommit.shortHash }} · {{ selectedCommit.author }}</span>
                </div>
                <div class="viewer-content">
                  <pre class="code-block diff-block"><code v-html="formatDiff(commitFileDiff)"></code></pre>
                </div>
              </template>
            </template>
            <!-- 标签模式 -->
            <template v-else-if="activeTab === 'tags'">
              <div class="empty-state small">
                <TagsOutlined :style="{ fontSize: '48px', color: 'var(--text-muted)', marginBottom: '8px' }" />
                <span style="color: var(--text-muted)">在左侧管理项目标签</span>
                <span style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">共 {{ tags.length }} 个标签</span>
              </div>
            </template>
            <!-- Release 详情模式 -->
            <template v-else-if="activeTab === 'releases'">
              <div v-if="selectedRelease" class="release-detail-panel">
                <div class="release-detail-header">
                  <h3 style="margin: 0; font-size: 16px;">{{ selectedRelease.name || selectedRelease.tagName }}</h3>
                  <div style="display: flex; gap: 6px; margin-top: 4px;">
                    <a-tag color="green">{{ selectedRelease.tagName }}</a-tag>
                    <a-tag v-if="selectedRelease.draft" color="orange">草稿</a-tag>
                    <a-tag v-if="selectedRelease.prerelease" color="blue">预发布</a-tag>
                    <span v-if="selectedRelease.publishedAt || selectedRelease.createdAt" style="color: var(--text-muted); font-size: 12px; line-height: 22px;">
                      {{ formatTime(selectedRelease.publishedAt || selectedRelease.createdAt) }}
                    </span>
                  </div>
                </div>
                <div v-if="selectedRelease.body" class="release-detail-body">
                  <pre style="white-space: pre-wrap; font-size: 13px; line-height: 1.6; margin: 0; font-family: inherit;">{{ selectedRelease.body }}</pre>
                </div>
                <div v-if="selectedRelease.assets?.length" class="release-detail-assets">
                  <div style="font-weight: 500; font-size: 13px; margin-bottom: 8px;">附件 ({{ selectedRelease.assets.length }})</div>
                  <div
                    v-for="asset in selectedRelease.assets"
                    :key="asset.name"
                    class="release-asset-item"
                  >
                    <DownloadOutlined style="color: var(--accent); flex-shrink: 0;" />
                    <span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">{{ asset.name }}</span>
                    <span style="color: var(--text-muted); font-size: 11px; flex-shrink: 0;">{{ formatFileSize(asset.size) }}</span>
                  </div>
                </div>
              </div>
              <div v-else class="empty-state small">
                <DownloadOutlined :style="{ fontSize: '48px', color: 'var(--text-muted)', marginBottom: '8px' }" />
                <span style="color: var(--text-muted)">选择一个 Release 查看详情</span>
                <span style="color: var(--text-muted); font-size: 12px; margin-top: 4px;">共 {{ releases.length }} 个 Release</span>
              </div>
            </template>
          </div>
        </div>
      </template>
    </div>

    <!-- 设置弹窗 -->
    <a-modal
      v-model:open="showSettings"
      title="设置"
      :width="480"
      @ok="saveSettings"
      ok-text="保存"
      cancel-text="取消"
      :ok-button-props="{ loading: settingsLoading }"
    >
      <a-form layout="vertical" :style="{ marginTop: '16px' }">
        <div style="margin-bottom: 16px; font-weight: 600; color: var(--text-secondary); font-size: 13px;">
          <UserOutlined /> Git 全局配置
        </div>
        <a-form-item label="user.name">
          <a-input v-model:value="gitConfig.userName" placeholder="Your Name" />
        </a-form-item>
        <a-form-item label="user.email">
          <a-input v-model:value="gitConfig.userEmail" placeholder="you@example.com" />
        </a-form-item>
      </a-form>
    </a-modal>
  </div>
</template>

<style scoped>
.content-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  height: 100%;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  color: var(--text-muted);
  gap: 8px;
}

.empty-state.small {
  font-size: 13px;
}

/* 项目头部 */
.project-view {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.project-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-secondary);
  gap: 12px;
  --wails-draggable: drag;
}

.project-info {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  flex: 1;
}

.project-name {
  font-size: 15px;
  font-weight: 600;
  white-space: nowrap;
}

.remote-url {
  font-size: 12px;
  color: var(--text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.remote-selector {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  padding: 1px 6px;
  border-radius: 4px;
  color: var(--accent, #89b4fa);
  font-weight: 500;
  transition: background 0.2s;
}
.remote-selector:hover {
  background: var(--hover-bg, rgba(137, 180, 250, 0.1));
}

.remote-url-text {
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.remote-dropdown {
  background: var(--dropdown-bg, #1e1e2e);
  border: 1px solid var(--border-color, #313244);
  border-radius: 8px;
  padding: 8px 0;
  min-width: 280px;
  max-width: 400px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
.remote-add-btn {
  padding: 8px 12px;
  color: var(--accent, #89b4fa);
  cursor: pointer;
  font-size: 12px;
  border-top: 1px solid var(--border-color, #313244);
  margin-top: 4px;
}
.remote-add-btn:hover {
  background: var(--hover-bg, rgba(137, 180, 250, 0.08));
}
.remote-add-form {
  padding: 8px 12px;
  border-top: 1px solid var(--border-color, #313244);
  margin-top: 4px;
}

.project-actions {
  flex-shrink: 0;
}

/* 代理设置下拉 */
.proxy-dropdown {
  background: var(--dropdown-bg, #1e1e2e);
  border: 1px solid var(--border-color, #313244);
  border-radius: 8px;
  padding: 4px 0;
  min-width: 180px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}
.proxy-dropdown-title {
  padding: 6px 12px;
  font-size: 11px;
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  border-bottom: 1px solid var(--border-color, #313244);
}
.proxy-dropdown-item {
  padding: 8px 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-primary, #cdd6f4);
  transition: background 0.15s;
}
.proxy-dropdown-item:hover {
  background: var(--hover-bg, rgba(137, 180, 250, 0.08));
}
.proxy-dropdown-item.active {
  color: var(--accent, #89b4fa);
}

/* 主体区域 */
.project-body {
  flex: 1;
  display: flex;
  overflow: hidden;
}

/* 文件列表 */
.file-list {
  border-right: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  min-width: 180px;
}

.resize-handle-inner {
  width: 4px;
  cursor: col-resize;
  background: transparent;
  flex-shrink: 0;
  position: relative;
  z-index: 10;
  transition: background 0.15s;
}

.resize-handle-inner:hover,
.resize-handle-inner:active {
  background: var(--accent, #89b4fa);
}

/* 提交区域 */
.commit-box {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-color);
}

.commit-input-row {
  display: flex;
  gap: 6px;
  align-items: center;
}

/* 分组区域 */
.section-group {
  border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.06));
}

.section-group:last-child {
  border-bottom: none;
}

.section-header {
  display: flex;
  align-items: center;
  padding: 5px 10px;
  cursor: pointer;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.3px;
  background: var(--bg-secondary, #181825);
  transition: background 0.12s;
  user-select: none;
}

.section-header:hover {
  background: var(--bg-hover);
}

.section-arrow {
  font-size: 10px;
  color: var(--text-muted);
  flex-shrink: 0;
}

.section-title {
  white-space: nowrap;
}

.section-actions {
  display: flex;
  visibility: hidden;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}

.section-header:hover .section-actions {
  visibility: visible;
}

.section-action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 4px;
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.12s;
}

.section-action-btn:hover {
  background: var(--bg-active, rgba(255,255,255,0.12));
  color: var(--text-primary);
}

.section-action-btn.danger:hover {
  background: rgba(243, 139, 168, 0.2);
  color: var(--danger, #f38ba8);
}

.file-list-content {
  flex: 1;
  overflow-y: auto;
  padding: 0;
}

/* 文件查看器 */
.file-viewer {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.viewer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 12px;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-secondary);
}

.viewer-path {
  font-size: 13px;
  color: var(--text-secondary);
  font-family: 'Consolas', 'Courier New', monospace;
}

.viewer-content {
  flex: 1;
  overflow: auto;
  padding: 0;
}

.code-block {
  margin: 0;
  padding: 12px;
  font-family: 'Consolas', 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.6;
  white-space: pre;
  tab-size: 4;
  color: var(--text-primary);
  user-select: text;
  -webkit-user-select: text;
}

:deep(.diff-add) {
  background: rgba(166, 227, 161, 0.15);
  color: var(--success);
  display: inline-block;
  width: 100%;
}

:deep(.diff-del) {
  background: rgba(243, 139, 168, 0.15);
  color: var(--danger);
  display: inline-block;
  width: 100%;
}

:deep(.diff-hunk) {
  color: var(--accent);
  display: inline-block;
  width: 100%;
}

@keyframes pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 0.8; }
}

/* 分支选择器 */
.branch-tag {
  cursor: pointer;
  user-select: none;
}

.branch-dropdown {
  background: var(--bg-surface, #252536);
  border: 1px solid var(--border-color, #313244);
  border-radius: 6px;
  min-width: 260px;
  max-height: 400px;
  overflow: hidden;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
}

.branch-create-box {
  display: flex;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-color);
}

.branch-dropdown-title {
  padding: 8px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.3px;
}

.branch-dropdown-list {
  max-height: 200px;
  overflow-y: auto;
}

.branch-dropdown-item {
  display: flex;
  align-items: center;
  padding: 0 4px 0 0;
  font-size: 13px;
  color: var(--text-primary);
  transition: background 0.12s;
}

.branch-dropdown-item:hover {
  background: var(--bg-hover);
}

.branch-dropdown-item.active {
  background: var(--bg-active);
  color: var(--success, #a6e3a1);
}

.branch-item-main {
  display: flex;
  align-items: center;
  flex: 1;
  min-width: 0;
  padding: 7px 8px 7px 12px;
  cursor: pointer;
}

.branch-item-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

.branch-item-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  visibility: hidden;
}

.branch-dropdown-item:hover .branch-item-actions {
  visibility: visible;
}

.branch-action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  font-size: 12px;
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.12s;
}

.branch-action-btn:hover {
  background: var(--bg-active, rgba(255,255,255,0.12));
  color: var(--accent, #89b4fa);
}

.branch-action-btn.danger:hover {
  background: rgba(243, 139, 168, 0.2);
  color: var(--danger, #f38ba8);
}

/* Stash 下拉 */
.stash-dropdown {
  background: var(--bg-surface, #252536);
  border: 1px solid var(--border-color, #313244);
  border-radius: 6px;
  min-width: 300px;
  max-height: 360px;
  overflow: hidden;
  box-shadow: 0 4px 16px rgba(0,0,0,0.4);
}

.stash-save-section {
  display: flex;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-color);
}

.stash-list-title {
  padding: 8px 12px 4px;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  gap: 4px;
}

.stash-list {
  max-height: 240px;
  overflow-y: auto;
}

.stash-item {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  font-size: 12px;
  border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.04));
  transition: background 0.12s;
}

.stash-item:hover {
  background: var(--bg-hover);
}

.stash-item-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.stash-ref {
  font-family: 'Consolas', 'Courier New', monospace;
  color: var(--accent, #89b4fa);
  font-size: 11px;
}

.stash-msg {
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.stash-item-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  visibility: hidden;
}

.stash-item:hover .stash-item-actions {
  visibility: visible;
}

.stash-action-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  font-size: 12px;
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.12s;
}

.stash-action-btn:hover {
  background: var(--bg-active, rgba(255,255,255,0.12));
  color: var(--accent, #89b4fa);
}

.stash-action-btn.danger:hover {
  background: rgba(243, 139, 168, 0.2);
  color: var(--danger, #f38ba8);
}

/* Tab 切换 */
.file-list-tabs {
  display: flex;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-secondary, #181825);
  flex-shrink: 0;
}

.file-list-tab {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 7px 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: all 0.15s;
  user-select: none;
}

.file-list-tab:hover {
  color: var(--text-secondary);
  background: var(--bg-hover);
}

.file-list-tab.active {
  color: var(--accent, #89b4fa);
  border-bottom-color: var(--accent, #89b4fa);
}

/* 提交历史项 */
.commit-item {
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.04));
  transition: background 0.12s;
}

.commit-item:hover {
  background: var(--bg-hover);
}

.commit-item.active {
  background: var(--bg-active);
}

.commit-msg-row {
  display: flex;
  align-items: center;
  gap: 4px;
}

.commit-msg-row .commit-msg {
  flex: 1;
  min-width: 0;
}

.commit-action-btns {
  display: flex;
  align-items: center;
  gap: 0;
  flex-shrink: 0;
  visibility: hidden;
}

.commit-action-btn {
  color: var(--text-muted) !important;
  width: 24px;
  height: 24px;
}

.commit-action-btn:hover {
  color: #f38ba8 !important;
}

.commit-item:hover .commit-action-btns {
  visibility: visible;
}

/* 未推送标识 */
.commit-unpushed-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #a6e3a1;
  flex-shrink: 0;
  margin-right: 2px;
}

.commit-push-tag {
  font-size: 10px;
  padding: 0 5px;
  border-radius: 3px;
  background: rgba(166, 227, 161, 0.15);
  color: #a6e3a1;
  line-height: 16px;
  flex-shrink: 0;
}

.commit-item.unpushed {
  border-left: 2px solid #a6e3a1;
}

.commit-msg {
  font-size: 13px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}

.commit-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 2px;
}

.commit-hash {
  font-family: 'Consolas', 'Courier New', monospace;
  color: var(--accent, #89b4fa);
}

.commit-author {
  display: flex;
  align-items: center;
  gap: 3px;
}

.commit-time {
  margin-left: auto;
}

/* 提交历史面板布局 */
.history-panel {
  display: flex;
  flex-direction: column;
}

.commit-list-section {
  overflow-y: auto;
  flex: 1;
  min-height: 80px;
}

.commit-list-section.has-selected {
  flex: none;
  max-height: 45%;
  border-bottom: none;
}

.commit-files-divider {
  padding: 6px 12px;
  background: var(--bg-secondary, #1e1e2e);
  border-top: 1px solid var(--border-color, rgba(255,255,255,0.06));
  border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.06));
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.commit-files-title {
  display: flex;
  align-items: center;
  gap: 6px;
}

.commit-files-section {
  overflow-y: auto;
  flex: 1;
  min-height: 60px;
}

.commit-file-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text-primary);
  transition: background 0.12s;
}

.commit-file-item:hover {
  background: var(--bg-hover);
}

.commit-file-item.active {
  background: var(--bg-active);
}

.commit-file-item.dir {
  color: var(--text-secondary);
  font-weight: 500;
}

.commit-file-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1;
}

.commit-file-count {
  font-size: 11px;
  color: var(--text-muted);
  margin-left: 4px;
  flex-shrink: 0;
}

/* 标签管理面板 */
.tags-panel {
  display: flex;
  flex-direction: column;
}

.tag-create-box {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-color);
}

.tag-list-section {
  flex: 1;
  overflow-y: auto;
}

.tag-item {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.04));
  transition: background 0.12s;
}

.tag-item:hover {
  background: var(--bg-hover);
}

.tag-main-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.tag-icon {
  color: var(--accent, #89b4fa);
  font-size: 14px;
  flex-shrink: 0;
}

.tag-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tag-action-btns {
  display: flex;
  align-items: center;
  gap: 0;
  flex-shrink: 0;
  visibility: hidden;
}

.tag-item:hover .tag-action-btns {
  visibility: visible;
}

.tag-action-btn {
  width: 24px;
  height: 24px;
  color: var(--text-muted) !important;
}

.tag-action-btn.push:hover {
  color: var(--accent, #89b4fa) !important;
}

.tag-action-btn.delete:hover {
  color: var(--danger, #f38ba8) !important;
}

.tag-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 2px;
  padding-left: 20px;
}

.tag-hash {
  font-family: 'Consolas', 'Courier New', monospace;
  color: var(--accent, #89b4fa);
}

.tag-message {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tag-time {
  margin-left: auto;
  flex-shrink: 0;
}

/* 冲突处理 */
.conflict-banner {
  border-bottom: 1px solid var(--border-color);
  background: rgba(250, 179, 135, 0.08);
}

.conflict-banner-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  font-size: 13px;
}

.conflict-file-list {
  max-height: 150px;
  overflow-y: auto;
  border-top: 1px solid var(--border-color);
}

.conflict-file-item {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  font-size: 12px;
  cursor: pointer;
  transition: background 0.12s;
}

.conflict-file-item:hover {
  background: var(--bg-hover);
}

.conflict-file-item.active {
  background: var(--bg-active);
}

.conflict-file-name {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: 'Consolas', 'Courier New', monospace;
  color: var(--text-secondary);
}

.conflict-file-actions {
  visibility: hidden;
  flex-shrink: 0;
}

.conflict-file-item:hover .conflict-file-actions {
  visibility: visible;
}

.conflict-resolve-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  font-size: 13px;
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.12s;
}

.conflict-resolve-btn:hover {
  background: rgba(166, 227, 161, 0.2);
  color: var(--success, #a6e3a1);
}

.conflict-editor {
  display: flex;
  flex-direction: column;
}

.conflict-textarea {
  flex: 1;
  width: 100%;
  background: var(--bg-primary, #1e1e2e);
  color: var(--text-primary);
  border: none;
  outline: none;
  resize: none;
  padding: 12px;
  font-family: 'Consolas', 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.6;
  tab-size: 4;
}

/* 提交搜索 */
.search-bar {
  display: flex;
  gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
}

/* Release 详情面板 */
.release-detail-panel {
  padding: 16px;
  overflow-y: auto;
  height: 100%;
}

.release-detail-header {
  margin-bottom: 16px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border-color);
}

.release-detail-body {
  margin-bottom: 16px;
  padding: 12px;
  background: var(--bg-secondary, #181825);
  border-radius: 6px;
  border: 1px solid var(--border-color);
}

.release-detail-assets {
  padding-top: 12px;
  border-top: 1px solid var(--border-color);
}

.release-asset-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 4px;
  font-size: 13px;
}

.release-asset-item:hover {
  background: var(--bg-hover, #313244);
}
</style>
