function apiData(resp) {
    if (!resp || typeof resp !== 'object') return null;
    if (resp.success && resp.data && typeof resp.data === 'object') return resp.data;
    return null;
}

function __gitCommitShortId(hash) {
    return String(hash || '').slice(0, 8);
}

function __gitTruncate(text, maxLen) {
    const s = String(text || '');
    if (s.length <= maxLen) return { text: s, truncated: false };
    return { text: s.slice(0, maxLen) + '…', truncated: true };
}

function __gitCommitUrl(repoPath, commitHash) {
    const hash = String(commitHash || '');
    const path = String(repoPath || '');
    return '/commit/' + encodeURIComponent(hash) + '?repoPath=' + encodeURIComponent(path);
}

function __gitPrepareCommitNavigation(repoPath, listEl, commitHash) {
    __gitSaveScroll(repoPath, listEl);
    __gitSetReturnFlag(repoPath);
    return __gitCommitUrl(repoPath, commitHash);
}

function __gitNextActiveIndex(currentIndex, key, total) {
    const t = Number(total || 0);
    if (!Number.isFinite(t) || t <= 0) return -1;

    let idx = Number.isFinite(currentIndex) ? currentIndex : -1;
    if (idx < 0) idx = 0;
    if (idx >= t) idx = t - 1;

    if (key === 'ArrowDown') return Math.min(t - 1, idx + 1);
    if (key === 'ArrowUp') return Math.max(0, idx - 1);
    return idx;
}

function __gitScrollKey(repoPath) {
    return 'gitListScroll:' + String(repoPath || '');
}

function __gitSaveScroll(repoPath, listEl) {
    try {
        if (!window.sessionStorage) return;
        if (!listEl) return;
        window.sessionStorage.setItem(__gitScrollKey(repoPath), String(listEl.scrollTop || 0));
    } catch (e) {
        return;
    }
}

function __gitLoadScroll(repoPath) {
    try {
        if (!window.sessionStorage) return 0;
        const v = window.sessionStorage.getItem(__gitScrollKey(repoPath));
        const n = parseInt(v || '0', 10);
        return Number.isFinite(n) && n >= 0 ? n : 0;
    } catch (e) {
        return 0;
    }
}

function __gitSetReturnFlag(repoPath) {
    try {
        if (!window.sessionStorage) return;
        window.sessionStorage.setItem('gitCommitReturn', JSON.stringify({ repoPath: String(repoPath || ''), at: Date.now() }));
    } catch (e) {
        return;
    }
}

function __gitConsumeReturnFlag() {
    try {
        if (!window.sessionStorage) return null;
        const raw = window.sessionStorage.getItem('gitCommitReturn');
        if (!raw) return null;
        window.sessionStorage.removeItem('gitCommitReturn');
        const payload = JSON.parse(raw);
        if (!payload || typeof payload !== 'object') return null;
        const repoPath = payload.repoPath;
        if (!repoPath || typeof repoPath !== 'string') return null;
        return repoPath;
    } catch (e) {
        return null;
    }
}

function __gitListCss() {
    return '<style>' +
        '.git-shell { display:flex; flex-direction:column; gap:10px; }' +
        '.git-top { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }' +
        '.git-select { padding:8px 12px; border:1px solid #d0d7de; border-radius:8px; background:#fff; font-size:14px; flex:1; }' +
        '.git-list { max-height: calc(70vh - 120px); overflow:auto; outline:none; }' +
        '.git-item { padding:10px 12px; border-bottom:1px solid #eee; cursor:pointer; display:flex; flex-direction:column; gap:4px; }' +
        '.git-item:hover { background:#f6f8fa; }' +
        '.git-item.active { background:#ddf4ff; }' +
        '.git-meta { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }' +
        '.git-time { font-size:12px; color:#0969da; font-family: ui-monospace, monospace; }' +
        '.git-hash { font-size:11px; color:#57606a; font-family: ui-monospace, monospace; cursor:pointer; }' +
        '.git-hash:hover { color:#0969da; text-decoration:underline; }' +
        '.git-author { font-size:12px; color:#57606a; }' +
        '.git-subject { font-size:13px; color:#24292f; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }' +
        '.git-link-group { display:inline-flex; align-items:stretch; border:1px solid #d0d7de; border-radius:999px; overflow:hidden; }' +
        '.git-link-btn { border:0; background:#f6f8fa; color:#0969da; padding:4px 10px; cursor:pointer; font-size:12px; line-height:18px; }' +
        '.git-link-btn + .git-link-btn { border-left:1px solid #d0d7de; }' +
        '.git-link-btn:hover { background:#ddf4ff; }' +
        '.git-diff-btn { border:1px solid #d0d7de; background:#fff; border-radius:6px; padding:4px 10px; cursor:pointer; font-size:12px; }' +
        '.git-diff-btn:hover { background:#f6f8fa; }' +
        '.git-pull-btn { border:1px solid #d0d7de; background:#fff; border-radius:6px; padding:4px 10px; cursor:pointer; font-size:12px; }' +
        '.git-pull-btn:hover { background:#f6f8fa; }' +
        '.git-pull-btn[disabled] { opacity:0.65; cursor:not-allowed; }' +
        '.git-push-btn { border:1px solid #d0d7de; background:#fff; border-radius:6px; padding:4px 10px; cursor:pointer; font-size:12px; }' +
        '.git-push-btn:hover { background:#f6f8fa; }' +
        '.git-push-btn[disabled] { opacity:0.65; cursor:not-allowed; }' +
        '.git-diffstat-table { width:100%; border-collapse:collapse; font-size:12px; }' +
        '.git-diffstat-table th, .git-diffstat-table td { padding:8px 10px; border-bottom:1px solid #eee; vertical-align:top; }' +
        '.git-diffstat-table th { background:#f6f8fa; color:#57606a; font-weight:600; text-align:left; }' +
        '.git-diffstat-path { font-family: ui-monospace, monospace; word-break:break-all; }' +
        '.git-diffstat-num { font-family: ui-monospace, monospace; text-align:right; }' +
        '.git-skeleton { padding:10px 12px; border-bottom:1px solid #eee; }' +
        '.git-skel-line { height:10px; border-radius:6px; background: linear-gradient(90deg, #f6f8fa 0%, #eaeef2 40%, #f6f8fa 80%); background-size: 240px 100%; animation: gitShimmer 1.2s infinite linear; }' +
        '.git-skel-line.sm { width: 55%; margin-top: 8px; height: 11px; }' +
        '.git-skel-line.md { width: 75%; }' +
        '.git-skel-line.lg { width: 92%; }' +
        '@keyframes gitShimmer { 0% { background-position: -240px 0; } 100% { background-position: 240px 0; } }' +
        '.git-banner { display:flex; gap:10px; align-items:center; justify-content:space-between; padding:10px 12px; border:1px solid #ff818266; background:#ffebe9; color:#cf222e; border-radius:8px; }' +
        '.git-banner button { border:1px solid #d0d7de; background:#fff; border-radius:6px; padding:6px 10px; cursor:pointer; font-size:12px; }' +
        '.git-empty { padding:28px 16px; text-align:center; color:#57606a; }' +
        '.git-empty svg { display:block; margin:0 auto 10px; opacity:0.9; }' +
        '.git-foot { padding:10px 12px; text-align:center; color:#6e7781; font-size:12px; }' +
        '</style>';
}

function __gitDirtyLineHtml(hasChanges, statusText, changeInfo) {
    const color = hasChanges ? '#cf222e' : '#2da44e';
    const pullText = typeof I18n !== 'undefined' ? I18n.t('git.pull') : 'Pull';
    const pushText = typeof I18n !== 'undefined' ? I18n.t('git.push_changes') : 'Push Changes';
    const btn = hasChanges
        ? '<button type="button" class="git-diff-btn" id="gitDiffBtn">diff</button><button type="button" class="git-pull-btn" id="gitPullBtn">' + pullText + '</button><button type="button" class="git-push-btn" id="gitPushBtn">' + pushText + '</button>'
        : '<button type="button" class="git-pull-btn" id="gitPullBtn">' + pullText + '</button>';
    const hint = hasChanges ? '<span id="gitPushHint" style="font-size:12px;color:#57606a;"></span>' : '';
    return '<div id="gitRepoDirtyLine" style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:12px;color:' + color + ';margin-top:4px;">' +
        '<span>' + escapeHtml(statusText || '') + escapeHtml(changeInfo || '') + '</span>' +
        btn +
        hint +
        '</div>';
}

function __gitBindPullButton(repoPath) {
    const btn = document.getElementById('gitPullBtn');
    if (!btn) return;
        btn.addEventListener('click', function() {
        if (btn.disabled) return;
        btn.disabled = true;
        const prevText = btn.textContent;
        btn.textContent = typeof I18n !== 'undefined' ? I18n.t('git.pulling') : 'Pulling...';
        const headers = authHeaders ? (authHeaders() || {}) : {};
        headers['Content-Type'] = 'application/json';
        fetch('/api/git/pull', {
            method: 'POST',
            headers: headers,
            body: JSON.stringify({ path: String(repoPath || '') })
        })
            .then(function(r) { return r.json(); })
            .then(function(resp) {
                const payload = apiData(resp);
                if (!payload) {
                    const msg = resp?.error?.message || (typeof I18n !== 'undefined' ? I18n.t('git.pull_failed') : 'Pull failed');
                    throw new Error(msg);
                }
                if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('git.pull_success') : 'Pull successful', 'success');
                window.loadGitList(repoPath);
            })
            .catch(function(e) {
                const msg = e?.message || (typeof I18n !== 'undefined' ? I18n.t('git.pull_failed') : 'Pull failed');
                if (typeof showToast === 'function') showToast(msg, 'error');
            })
            .finally(function() {
                btn.disabled = false;
                btn.textContent = prevText;
            });
    });
}

function __gitBindPushButton(repoPath) {
    const btn = document.getElementById('gitPushBtn');
    if (!btn) return;
    const hint = document.getElementById('gitPushHint');
    btn.addEventListener('click', function() {
        __gitPushChangesWithRemoteFlow(repoPath, { btn: btn, hint: hint });
    });
}

function __gitBindDiffButton(repoPath) {
    const btn = document.getElementById('gitDiffBtn');
    if (!btn) return;
    btn.addEventListener('click', function() {
        const url = '/git/diff?repoPath=' + encodeURIComponent(String(repoPath || ''));
        try {
            window.open(url, '_blank', 'noopener');
        } catch (e) {
            window.location.href = url;
        }
    });
}

// 直接执行 Diff：打开新窗口显示 diff
function __gitDoDiff(repoPath) {
    const url = '/git/diff?repoPath=' + encodeURIComponent(String(repoPath || ''));

    // 显示 loading
    if (typeof window.showTaskListener === 'function') {
        window.showTaskListener(typeof I18n !== 'undefined' ? I18n.t('git.loading_diff') : 'Loading diff...');
    }
    window.open(url, '_blank');

    // 延迟隐藏
    setTimeout(function() {
        if (typeof window.hideTaskListener === 'function') {
            window.hideTaskListener();
        }
    }, 1000);
}

// Checkout 放弃所有更改
function __gitDoCheckout(repoPath) {
    window.showConfirm(
        typeof I18n !== 'undefined' ? I18n.t('git.checkout_confirm_title') : 'Discard Local Changes',
        typeof I18n !== 'undefined' ? I18n.t('git.checkout_confirm_message') : 'Are you sure you want to discard all local changes? This action cannot be undone!',
        function() {
            const headers = authHeaders ? (authHeaders() || {}) : {};
            headers['Content-Type'] = 'application/json';

            if (typeof window.showTaskListener === 'function') {
                window.showTaskListener(typeof I18n !== 'undefined' ? I18n.t('git.checkout_progress') : 'Checking out...');
            }

            fetch('/api/git/checkout', {
                method: 'POST',
                headers: headers,
                body: JSON.stringify({ path: String(repoPath || ''), all: true })
            })
                .then(function(r) { return r.json(); })
                .then(function(resp) {
                    const payload = apiData(resp);
                    if (typeof window.hideTaskListener === 'function') {
                        window.hideTaskListener();
                    }
                    const msg = payload && payload.message ? payload.message : (resp.error && resp.error.message ? resp.error.message : (typeof I18n !== 'undefined' ? I18n.t('git.operation_complete') : 'Operation complete'));
                    if (typeof window.showTaskListener === 'function') {
                        window.showTaskListener(msg);
                    }
                    // 刷新列表
                    __gitLoadDiffFileList(repoPath, { has_changes: false });
                    window.loadGitList();
                })
                .catch(function() {
                    if (typeof window.hideTaskListener === 'function') {
                        window.hideTaskListener();
                    }
                });
        }
    );
}

// 直接执行 Pull
function __gitDoPull(repoPath) {
    const headers = authHeaders ? (authHeaders() || {}) : {};
    headers['Content-Type'] = 'application/json';

    // 显示 loading
    if (typeof window.showTaskListener === 'function') {
        window.showTaskListener(typeof I18n !== 'undefined' ? I18n.t('git.pulling') : 'Pulling...');
    }

    fetch('/api/git/pull', {
        method: 'POST',
        headers: headers,
        body: JSON.stringify({ path: String(repoPath || '') })
    })
        .then(function(r) { return r.json(); })
        .then(function(resp) {
            const payload = apiData(resp);
            if (typeof window.hideTaskListener === 'function') {
                window.hideTaskListener();
            }

            if (!payload) {
                const msg = resp?.error?.message || (typeof I18n !== 'undefined' ? I18n.t('git.pull_failed') : 'Pull failed');
                throw new Error(msg);
            }
            if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('git.pull_success') : 'Pull successful', 'success');
            window.loadGitList(repoPath);
        })
        .catch(function(e) {
            if (typeof window.hideTaskListener === 'function') {
                window.hideTaskListener();
            }
            const msg = e?.message || (typeof I18n !== 'undefined' ? I18n.t('git.pull_failed') : 'Pull failed');
            if (typeof showToast === 'function') showToast(msg, 'error');
        });
}

function __gitFetchRemotes(repoPath) {
    const headers = authHeaders ? (authHeaders() || {}) : {};
    return fetch('/api/git/remotes?path=' + encodeURIComponent(String(repoPath || '')), { headers: headers })
        .then(function(r) { return r.json(); })
        .then(function(resp) {
            const payload = apiData(resp);
            if (!payload) {
                const msg = resp?.error?.message || (typeof I18n !== 'undefined' ? I18n.t('git.fetch_remotes_failed') : 'Failed to fetch remotes');
                throw new Error(msg);
            }
            const remotes = payload.remotes;
            if (!Array.isArray(remotes)) return [];
            return remotes.filter(function(r) { return typeof r === 'string' && r.trim(); });
        });
}

function __gitRunPushChanges(repoPath, remote, ui) {
    const headers = authHeaders ? (authHeaders() || {}) : {};
    headers['Content-Type'] = 'application/json';
    const btn = ui && ui.btn ? ui.btn : null;
    const hint = ui && ui.hint ? ui.hint : null;
    const prevText = ui && typeof ui.prevText === 'string' ? ui.prevText : (btn ? btn.textContent : '');
    let didReload = false;

    if (btn) {
        btn.disabled = true;
        btn.textContent = typeof I18n !== 'undefined' ? I18n.t('git.pushing') : 'Pushing...';
    }
    if (hint) hint.textContent = typeof I18n !== 'undefined' ? I18n.t('common.processing') : 'Processing...';

    if (typeof window.showTaskListener === 'function') {
        window.showTaskListener(typeof I18n !== 'undefined' ? I18n.t('git.pushing') : 'Pushing...');
    }

    return fetch('/api/git/push-changes', {
        method: 'POST',
        headers: headers,
        body: JSON.stringify({ path: String(repoPath || ''), remote: String(remote || '') })
    })
        .then(function(r) { return r.json(); })
        .then(function(resp) {
            const payload = apiData(resp);
            if (typeof window.hideTaskListener === 'function') {
                window.hideTaskListener();
            }

            if (!payload) {
                const msg = resp?.error?.message || (typeof I18n !== 'undefined' ? I18n.t('git.push_failed') : 'Push failed');
                throw new Error(msg);
            }
            if (payload.status === 'clean') {
                if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('git.no_changes') : 'No changes to commit', 'warning');
                if (hint) hint.textContent = typeof I18n !== 'undefined' ? I18n.t('git.no_changes') : 'No changes to commit';
                return;
            }
            if (payload.pushed) {
                if (typeof showToast === 'function') showToast(typeof I18n !== 'undefined' ? I18n.t('git.push_success') : 'Push successful', 'success');
                didReload = true;
                window.loadGitList(repoPath);
                return;
            }
            const msg = payload.message || (typeof I18n !== 'undefined' ? I18n.t('git.push_failed') : 'Push failed');
            throw new Error(msg);
        })
        .catch(function(e) {
            if (typeof window.hideTaskListener === 'function') {
                window.hideTaskListener();
            }
            const msg = e?.message || (typeof I18n !== 'undefined' ? I18n.t('git.push_failed') : 'Push failed');
            if (typeof showToast === 'function') showToast(msg, 'error');
            if (hint) hint.textContent = msg;
            throw e;
        })
        .finally(function() {
            if (btn && !didReload) {
                btn.disabled = false;
                btn.textContent = prevText || (typeof I18n !== 'undefined' ? I18n.t('git.push_changes') : 'Push Changes');
            }
        });
}

function __gitPromptPushRemote(repoPath, remotes, onConfirm) {
    const list = Array.isArray(remotes) ? remotes : [];
    if (!list.length) return false;
    const defaultRemote = list.indexOf('origin') >= 0 ? 'origin' : list[0];
    if (typeof window.openDialogDrawer !== 'function') return false;

    window.openDialogDrawer({
        title: typeof I18n !== 'undefined' ? I18n.t('git.select_remote_title') : 'Select Remote',
        message: typeof I18n !== 'undefined' ? I18n.t('git.select_remote_message') : 'Please select which remote to push to',
        confirmText: typeof I18n !== 'undefined' ? I18n.t('git.push') : 'Push',
        select: {
            options: list.map(function(r) { return { value: r, label: r }; }),
            defaultValue: defaultRemote
        },
        onConfirm: function(result) {
            const selected = result && typeof result.select === 'string' ? result.select : defaultRemote;
            if (typeof onConfirm === 'function') onConfirm(selected);
        }
    });
    return true;
}

function __gitPushChangesWithRemoteFlow(repoPath, ui) {
    const btn = ui && ui.btn ? ui.btn : null;
    const hint = ui && ui.hint ? ui.hint : null;
    const prevText = btn ? btn.textContent : '';
    if (ui && typeof ui === 'object') ui.prevText = prevText;

    if (btn) {
        btn.disabled = true;
        btn.textContent = typeof I18n !== 'undefined' ? I18n.t('git.checking_remotes') : 'Checking remotes...';
    }
    if (hint) hint.textContent = typeof I18n !== 'undefined' ? I18n.t('common.processing') : 'Processing...';

    __gitFetchRemotes(repoPath)
        .then(function(remotes) {
            if (!remotes.length) {
                const msg = typeof I18n !== 'undefined' ? I18n.t('git.no_remotes') : 'No remotes found';
                if (typeof showToast === 'function') showToast(msg, 'error');
                if (hint) hint.textContent = msg;
                return;
            }
            if (remotes.length === 1) {
                return __gitRunPushChanges(repoPath, remotes[0], ui);
            }

            if (btn) {
                btn.disabled = false;
                btn.textContent = prevText;
            }
            if (hint) hint.textContent = '';

            const opened = __gitPromptPushRemote(repoPath, remotes, function(selected) {
                __gitRunPushChanges(repoPath, selected, ui);
            });
            if (!opened) {
                const msg = typeof I18n !== 'undefined' ? I18n.t('git.cannot_open_dialog') : 'Cannot open dialog';
                if (typeof showToast === 'function') showToast(msg, 'error');
                if (hint) hint.textContent = msg;
            }
        })
        .catch(function(e) {
            const msg = e?.message || (typeof I18n !== 'undefined' ? I18n.t('git.push_failed') : 'Push failed');
            if (typeof showToast === 'function') showToast(msg, 'error');
            if (hint) hint.textContent = msg;
        })
        .finally(function() {
            if (btn && btn.disabled && btn.textContent === (typeof I18n !== 'undefined' ? I18n.t('git.checking_remotes') : 'Checking remotes...')) {
                btn.disabled = false;
                btn.textContent = prevText || (typeof I18n !== 'undefined' ? I18n.t('git.push_changes') : 'Push Changes');
            }
        });
}

// 直接执行 Push
function __gitDoPush(repoPath) {
    __gitFetchRemotes(repoPath)
        .then(function(remotes) {
            if (!remotes.length) {
                const msg = typeof I18n !== 'undefined' ? I18n.t('git.no_remotes') : 'No remotes found';
                if (typeof showToast === 'function') showToast(msg, 'error');
                throw new Error(msg);
            }
            if (remotes.length === 1) {
                return __gitRunPushChanges(repoPath, remotes[0], null);
            }
            const opened = __gitPromptPushRemote(repoPath, remotes, function(selected) {
                __gitRunPushChanges(repoPath, selected, null);
            });
            if (!opened) {
                const msg = typeof I18n !== 'undefined' ? I18n.t('git.cannot_open_dialog') : 'Cannot open dialog';
                if (typeof showToast === 'function') showToast(msg, 'error');
                throw new Error(msg);
            }
            return;
        })
        .catch(function() {});
}

function __gitRenderDiffFileTable(container, rows, repoPath, meta) {
    if (!container) return;
    const list = Array.isArray(rows) ? rows : [];
    const hasChanges = !!(meta && meta.has_changes);
    const changeInfo = meta && meta.change_info ? String(meta.change_info) : '';
    const statusHtml = hasChanges
        ? '<span style="font-size:12px;color:#e3b341;">⚠️ Dirty</span>'
        : '<span style="font-size:12px;color:#2da44e;">✓ Clean</span>';
    const infoHtml = changeInfo ? '<span style="font-size:11px;color:#666;margin-left:8px;">' + escapeHtml(changeInfo) + '</span>' : '';
    
    // 更新时间
    const now = Date.now();
    const timeAgo = function(ms) {
        if (!ms) return '-';
        const s = Math.floor(ms / 1000);
        if (s < 60) return s + 's ago';
        if (s < 3600) return Math.floor(s / 60) + 'm ago';
        if (s < 86400) return Math.floor(s / 3600) + 'h ago';
        return Math.floor(s / 86400) + 'd ago';
    };
    const updateTime = timeAgo(now - (window.__gitLastRefresh || now));
    window.__gitLastRefresh = now;
    
    if (!list.length) {
        container.innerHTML = '<div style="padding:10px 12px;color:#57606a;font-size:12px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;">' +
            statusHtml + infoHtml +
            '<span>' + (typeof I18n !== 'undefined' ? I18n.t('git.no_changes_yet') : 'No changes yet') + ' · ' + updateTime + '</span>' +
            '</div>';
        return;
    }

    const header = '<div style="margin:8px 0 4px;padding-left:4px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;font-size:12px;color:#57606a;">' +
        statusHtml + infoHtml + '<span style="margin-left:auto;">' + updateTime + '</span></div>';
    const table = '<div style="background:#fff;border:1px solid #d0d7de;border-radius:8px;overflow:hidden;margin:0 -16px;padding:0 16px;">' +
        '<table class="git-diffstat-table" style="width:100%;border-collapse:collapse;">' +
        list.map(function(r, idx) {
            const path = String((r && r.path) || '-');
            const pathEsc = escapeHtml(path);
            const add = (r && (r.added === null || r.added === undefined)) ? '-' : String(r.added || 0);
            const del = (r && (r.deleted === null || r.deleted === undefined)) ? '-' : String(r.deleted || 0);
            const addNum = add === '-' ? 0 : parseInt(add) || 0;
            const delNum = del === '-' ? 0 : parseInt(del) || 0;
            // 构建绝对路径：repoPath + path
            const repo = String(repoPath || '');
            const fullPath = repo + (repo.endsWith('/') ? '' : '/') + path;
            // 点击行：打开代码编辑器
            const rowClick = "__gitOpenFileEditor('" + fullPath + "')";
            // 点击 +/- 数字：查看 diff
            const addClick = addNum > 0 ? "__gitOpenFileDiff('" + repo + "', '" + path + "'); event.stopPropagation();" : '';
            const delClick = delNum > 0 ? "__gitOpenFileDiff('" + repo + "', '" + path + "'); event.stopPropagation();" : '';
            return '<tr onclick="' + rowClick + '" style="cursor:pointer;border-bottom:1px solid #eee;">' +
                '<td class="git-diffstat-path" title="' + pathEsc + '" style="color:#0969da;padding:8px 12px;text-align:left;">' + pathEsc + '</td>' +
                '<td class="git-diffstat-num" ' + (addClick ? 'onclick="' + addClick + '" style="cursor:pointer;color:#2da44e;padding:8px 12px;text-align:right;white-space:nowrap;text-decoration:underline;text-decoration-style:dotted;" title="' + (typeof I18n !== 'undefined' ? I18n.t('git.click_view_diff') : 'Click to view diff') + '"' : 'style="color:#2da44e;padding:8px 12px;text-align:right;white-space:nowrap;"') + '>+' + escapeHtml(add) + '</td>' +
                '<td class="git-diffstat-num" ' + (delClick ? 'onclick="' + delClick + '" style="cursor:pointer;color:#cf222e;padding:8px 12px;text-align:right;white-space:nowrap;text-decoration:underline;text-decoration-style:dotted;" title="' + (typeof I18n !== 'undefined' ? I18n.t('git.click_view_diff') : 'Click to view diff') + '"' : 'style="color:#cf222e;padding:8px 12px;text-align:right;white-space:nowrap;"') + '>-' + escapeHtml(del) + '</td>' +
                '</tr>';
        }).join('') +
        '</table></div>';

    container.innerHTML = header + table;
}

// 设置 home 路径
window.__gitHomePath = '/root';

// 打开文件到代码编辑器（新窗口）
function __gitOpenFileEditor(fullPath) {
    // 提取相对于 /root 的路径
    let relPath = fullPath;
    if (fullPath.startsWith('/root/')) {
        relPath = fullPath.slice(6); // 去掉 /root/
    }
    const url = '/edit/' + encodeURIComponent(relPath);
    window.open(url, '_blank');
}

// 打开单个文件的 diff
function __gitOpenFileDiff(repoPath, filePath) {
    const url = '/git/diff?repoPath=' + encodeURIComponent(repoPath) + '&file=' + encodeURIComponent(filePath);
    window.open(url, '_blank');
}

function __gitLoadDiffFileList(repoPath, meta) {
    window.__gitCurrentRepoPath = repoPath;  // 保存当前仓库路径用于自动刷新

    const el = document.getElementById('gitDiffFilesBox');
    if (!el) return;

    // 保存 meta 用于刷新
    let metaEl = document.getElementById('gitDiffMeta');
    if (metaEl) {
        metaEl.textContent = JSON.stringify(meta);
    } else {
        metaEl = document.createElement('div');
        metaEl.id = 'gitDiffMeta';
        metaEl.style.display = 'none';
        metaEl.textContent = JSON.stringify(meta || { has_changes: false });
        el.parentNode.insertBefore(metaEl, el);
    }

    el.innerHTML = '<div style="padding:10px 12px;color:#57606a;font-size:12px;">' + (typeof I18n !== 'undefined' ? I18n.t('git.loading_changes') : 'Loading changes...') + '</div>';
    fetch('/api/git/diff-numstat?path=' + encodeURIComponent(String(repoPath || '')), { headers: authHeaders() })
        .then(function(r) { return r.json(); })
        .then(function(resp) {
            const payload = apiData(resp);
            if (!payload) {
                const msg = resp && resp.error && resp.error.message ? resp.error.message : (typeof I18n !== 'undefined' ? I18n.t('common.load_failed') : 'Load failed');
                throw new Error(msg);
            }
            // 使用 API 返回的绝对路径
            const absRepoPath = payload.repoPath || repoPath;
            __gitRenderDiffFileTable(el, payload.files || [], absRepoPath, meta);
        })
        .catch(function() {
            el.innerHTML = '<div style="padding:10px 12px;color:#cf222e;font-size:12px;">' + (typeof I18n !== 'undefined' ? I18n.t('common.load_failed') : 'Load failed') + '</div>';
        });
}

function __gitSetDrawerTitle(html) {
    const el = document.getElementById('gitDrawerTitle');
    if (!el) return;
    el.innerHTML = html || (typeof I18n !== 'undefined' ? I18n.t('git.title') : 'Git Manager');
}

function __gitSkeletonHtml(rows) {
    const n = rows || 8;
    let html = '';
    for (let i = 0; i < n; i += 1) {
        html += '<div class="git-skeleton">' +
            '<div class="git-skel-line ' + (i % 3 === 0 ? 'lg' : i % 3 === 1 ? 'md' : 'lg') + '"></div>' +
            '<div class="git-skel-line sm"></div>' +
            '</div>';
    }
    return html;
}

function __gitEmptyHtml() {
    return '<div class="git-empty">' +
        '<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">' +
        '<path d="M18 22C18 18.686 20.686 16 24 16H48C51.314 16 54 18.686 54 22V50C54 53.314 51.314 56 48 56H24C20.686 56 18 53.314 18 50V22Z" stroke="#8C959F" stroke-width="2"/>' +
        '<path d="M26 27H46" stroke="#8C959F" stroke-width="2" stroke-linecap="round"/>' +
        '<path d="M26 35H42" stroke="#8C959F" stroke-width="2" stroke-linecap="round"/>' +
        '<path d="M26 43H38" stroke="#8C959F" stroke-width="2" stroke-linecap="round"/>' +
        '</svg>' +
        '<div style="font-size:14px;font-weight:600;margin-bottom:4px;">' + (typeof I18n !== 'undefined' ? I18n.t('git.empty.title') : 'No commits') + '</div>' +
        '<div style="font-size:12px;">' + (typeof I18n !== 'undefined' ? I18n.t('git.empty.subtitle') : 'This repository has no commits or log unavailable') + '</div>' +
        '</div>';
}

function __gitErrorBannerHtml(message) {
    const msg = escapeHtml(message || (typeof I18n !== 'undefined' ? I18n.t('common.load_failed') : 'Load failed'));
    return '<div class="git-banner" role="alert">' +
        '<div style="min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">' + msg + '</div>' +
        '<button type="button" id="gitRetryBtn">' + (typeof I18n !== 'undefined' ? I18n.t('common.retry') : 'Retry') + '</button>' +
        '</div>';
}

function __gitCommitItemHtml(commit, branch, repoPath, index) {
    const time = escapeHtml(commit.committed_at || '');
    const author = escapeHtml(commit.author || '');
    const subjectRaw = String(commit.subject || '');
    const trunc = __gitTruncate(subjectRaw, 60);
    const subject = escapeHtml(trunc.text || '');
    const hashFull = String(commit.hash || '');
    const shortHash = escapeHtml(__gitCommitShortId(hashFull));
    const hashEsc = escapeHtml(hashFull);
    const repoPathEsc = escapeHtml(repoPath || '');

    return '<div class="git-item" data-index="' + String(index) + '" data-hash="' + hashEsc + '" data-repo="' + repoPathEsc + '">' +
        '<div class="git-meta">' +
        '<span class="git-time">' + time + '</span>' +
        '<span class="git-hash" data-hash="' + hashEsc + '" data-repo="' + repoPathEsc + '">' + shortHash + '</span>' +
        '<span class="git-author" title="' + author + '">' + (author || '-') + '</span>' +
        (branch ? '<span style="background:#ddf4ff;color:#0969da;padding:1px 6px;border-radius:999px;font-size:10px;">' + escapeHtml(branch) + '</span>' : '') +
        '</div>' +
        '<div class="git-subject" title="' + escapeHtml(subjectRaw) + '">' + (subject || '-') + '</div>' +
        '</div>';
}

function __gitMountCommitList(container, repoPath, headerData) {
    const state = {
        repoPath: repoPath,
        page: 1,
        perPage: 20,
        maxCount: 50,
        hasMore: true,
        loading: false,
        activeIndex: -1,
        commits: []
    };

    const headerHtml = headerData && headerData.html ? headerData.html : '';
    container.innerHTML = __gitListCss() + '<div class="git-shell">' + headerHtml +
        '<div class="git-list" id="gitCommitList" tabindex="0" aria-label="Git commits">' +
        __gitSkeletonHtml(8) +
        '</div>' +
        '<div class="git-foot" id="gitCommitFoot" style="display:none;"></div>' +
        '</div>';

    const listEl = document.getElementById('gitCommitList');
    const footEl = document.getElementById('gitCommitFoot');

    function setFoot(text) {
        if (!footEl) return;
        if (!text) {
            footEl.style.display = 'none';
            footEl.textContent = '';
            return;
        }
        footEl.style.display = 'block';
        footEl.textContent = text;
    }

    function setActiveIndex(nextIndex, ensureVisible) {
        if (!listEl) return;
        const total = state.commits.length;
        if (total <= 0) {
            state.activeIndex = -1;
            return;
        }
        let idx = nextIndex;
        if (idx < 0) idx = 0;
        if (idx >= total) idx = total - 1;

        const prev = state.activeIndex;
        state.activeIndex = idx;
        if (prev >= 0) {
            const prevEl = listEl.querySelector('.git-item[data-index="' + String(prev) + '"]');
            if (prevEl) prevEl.classList.remove('active');
        }
        const curEl = listEl.querySelector('.git-item[data-index="' + String(idx) + '"]');
        if (curEl) curEl.classList.add('active');
        if (ensureVisible && curEl && typeof curEl.scrollIntoView === 'function') {
            curEl.scrollIntoView({ block: 'nearest' });
        }
    }

    function navigateToCommit(commitHash) {
        window.location.href = __gitPrepareCommitNavigation(state.repoPath, listEl, commitHash);
    }

    function renderInitial(commits) {
        if (!listEl) return;
        if (!commits || commits.length === 0) {
            listEl.innerHTML = __gitEmptyHtml();
            setFoot('');
            return;
        }
        listEl.innerHTML = commits.map(function(c, i) {
            return __gitCommitItemHtml(c, headerData && headerData.branch, state.repoPath, i);
        }).join('');

        const savedScrollTop = __gitLoadScroll(state.repoPath);
        if (savedScrollTop > 0) listEl.scrollTop = savedScrollTop;
        setActiveIndex(0, false);
        try { listEl.focus(); } catch (e) { return; }
    }

    function appendCommits(commits, startIndex) {
        if (!listEl) return;
        if (!commits || commits.length === 0) return;
        const html = commits.map(function(c, i) {
            return __gitCommitItemHtml(c, headerData && headerData.branch, state.repoPath, startIndex + i);
        }).join('');
        listEl.insertAdjacentHTML('beforeend', html);
    }

    function fetchPage(page) {
        if (state.loading) return;
        state.loading = true;
        setFoot(typeof I18n !== 'undefined' ? I18n.t('common.loading') : 'Loading...');
        fetch('/api/git/commit-list?path=' + encodeURIComponent(state.repoPath) +
            '&max_count=' + String(state.maxCount) +
            '&page=' + String(page) +
            '&per_page=' + String(state.perPage), { headers: authHeaders() })
            .then(function(r) { return r.json(); })
            .then(function(data) {
                const payload = apiData(data);
                if (!payload || !payload.is_repo) {
                    if (listEl) listEl.innerHTML = '<div style="padding:16px;text-align:center;color:#cf222e;">' + (typeof I18n !== 'undefined' ? I18n.t('git.not_a_repo') : 'This directory is not a Git repository') + '</div>';
                    setFoot('');
                    state.hasMore = false;
                    state.commits = [];
                    return;
                }

                const rows = Array.isArray(payload.commits) ? payload.commits : [];
                const pg = payload.pagination || {};
                state.hasMore = !!pg.has_more;
                state.page = pg.page ? pg.page : page;

                if (page === 1) {
                    state.commits = rows.slice();
                    renderInitial(state.commits);
                } else {
                    const startLen = state.commits.length;
                    rows.forEach(function(r) { state.commits.push(r); });
                    appendCommits(rows, startLen);
                }

                if (!state.hasMore) {
                    setFoot(state.commits.length > 0 ? (typeof I18n !== 'undefined' ? I18n.t('git.all_commits_loaded') : 'All commits loaded') : '');
                } else {
                    setFoot('');
                }
            })
            .catch(function() {
                if (!listEl) return;
                listEl.innerHTML = __gitErrorBannerHtml(typeof I18n !== 'undefined' ? I18n.t('git.api_error_commits') : 'API error: Unable to fetch commits');
                setFoot('');
                const btn = document.getElementById('gitRetryBtn');
                if (btn) btn.addEventListener('click', function() { __gitMountCommitList(container, repoPath, headerData); });
            })
            .finally(function() {
                state.loading = false;
            });
    }

    if (listEl) {
        listEl.addEventListener('scroll', function() {
            __gitSaveScroll(state.repoPath, listEl);
            if (!state.hasMore || state.loading) return;
            const threshold = 200;
            if (listEl.scrollTop + listEl.clientHeight >= listEl.scrollHeight - threshold) {
                fetchPage(state.page + 1);
            }
        });

        listEl.addEventListener('click', function(e) {
            const target = e.target;
            if (!target) return;
            const item = target.closest ? target.closest('.git-item') : null;
            if (item && item.getAttribute) {
                const idx = parseInt(item.getAttribute('data-index') || '-1', 10);
                if (Number.isFinite(idx) && idx >= 0) setActiveIndex(idx, false);
            }
            const hashEl = target.closest ? target.closest('.git-hash') : null;
            if (hashEl && hashEl.getAttribute) {
                const hash = hashEl.getAttribute('data-hash') || '';
                if (hash) navigateToCommit(hash);
            }
        });

        listEl.addEventListener('keydown', function(e) {
            if (!e) return;
            if (e.key === 'ArrowDown') {
                e.preventDefault();
                setActiveIndex(__gitNextActiveIndex(state.activeIndex, 'ArrowDown', state.commits.length), true);
            } else if (e.key === 'ArrowUp') {
                e.preventDefault();
                setActiveIndex(__gitNextActiveIndex(state.activeIndex, 'ArrowUp', state.commits.length), true);
            } else if (e.key === 'Enter') {
                e.preventDefault();
                if (state.activeIndex < 0 || state.activeIndex >= state.commits.length) return;
                const c = state.commits[state.activeIndex];
                if (c && c.hash) navigateToCommit(c.hash);
            }
        });
    }

    fetchPage(1);
}

window.loadGitList = function(specificRepoPath) {
    const container = document.getElementById('gitListContainer');
    if (container) container.innerHTML = __gitListCss() + '<div class="git-shell"><div class="git-list">' + __gitSkeletonHtml(8) + '</div></div>';
    __gitSetDrawerTitle(typeof I18n !== 'undefined' ? I18n.t('git.title') : 'Git Manager');

    if (specificRepoPath) {
        fetch('/api/git/repo-status?path=' + encodeURIComponent(specificRepoPath), { headers: authHeaders() })
            .then(function(r) { return r.json(); })
            .then(function(data) {
                const payload = apiData(data);
                if (!container) return;
                if (!payload || !payload.is_repo) {
                    container.innerHTML = __gitListCss() + '<div style="padding:16px;text-align:center;color:#cf222e;">' + (typeof I18n !== 'undefined' ? I18n.t('git.not_a_repo') : 'This directory is not a Git repository') + '</div>';
                    return;
                }

                const repo = payload;
                const repoName = (repo.name || specificRepoPath).replace(/\.git$/, '') + '.git';
                const repoPathArg = escapeHtml(JSON.stringify(String(specificRepoPath || '')));
                const repoStatus = repo.status || {};
                const hasChanges = repoStatus.has_changes || false;
                let changeInfo = '';
                if (hasChanges) {
                    const changes = [];
                    if (repoStatus.modified) changes.push((repoStatus.modified || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.modified') : 'Modified'));
                    if (repoStatus.added) changes.push((repoStatus.added || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.added') : 'Added'));
                    if (repoStatus.deleted) changes.push((repoStatus.deleted || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.deleted') : 'Deleted'));
                    if (repoStatus.untracked) changes.push((repoStatus.untracked || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.untracked') : 'Untracked'));
                    changeInfo = changes.length ? ' · ' + changes.join(', ') : '';
                }

                const titleHtml = '<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">' +
                    '<span style="font-weight:600;word-break:break-all;font-size:15px;">' + escapeHtml(repoName) + '</span>' +
                    '<div class="git-link-group">' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoDiff(' + repoPathArg + ');">diff</button>' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoCheckout(' + repoPathArg + ');">checkout</button>' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoPull(' + repoPathArg + ');">pull</button>' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoPush(' + repoPathArg + ');">push</button>' +
                    '</div>' +
                    '</div>';
                __gitSetDrawerTitle(titleHtml);

                const headerHtml = '<div style="padding:12px 12px 8px;"><div id="gitDiffFilesBox"></div></div>';
                const diffMeta = { has_changes: hasChanges, change_info: changeInfo };

                __gitMountCommitList(container, specificRepoPath, { html: headerHtml, branch: '' });
                __gitLoadDiffFileList(specificRepoPath, diffMeta);
            })
            .catch(function() {
                if (container) container.innerHTML = __gitErrorBannerHtml(typeof I18n !== 'undefined' ? I18n.t('common.load_failed') : 'Load failed');
            });
        return;
    }

    fetch('/api/git/list', { headers: authHeaders() })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            const payload = apiData(data);
            if (!container) return;
            if (!payload || !payload.repos || payload.repos.length === 0) {
                container.innerHTML = __gitListCss() + '<div class="git-shell">' + __gitEmptyHtml() + '</div>';
                return;
            }

            window.__gitRepos = payload.repos || [];
            const css = __gitListCss();
            const html = css + '<div class="git-shell"><div class="git-top"><span style="font-size:12px;color:#57606a;">' + (typeof I18n !== 'undefined' ? I18n.t('git.repository') : 'Repository') + '</span><select id="gitRepoSelect" class="git-select"></select></div><div id="gitRepoPanel"></div></div>';
            container.innerHTML = html;

            const repos = window.__gitRepos || [];
            const select = document.getElementById('gitRepoSelect');
            const panelEl = document.getElementById('gitRepoPanel');

            function renderRepo(repo) {
                if (!repo || !panelEl) return;
                const repoPath = repo.path || '';
                const repoName = (repo.name || repoPath).replace(/\.git$/, '') + '.git';
                const repoPathArg = escapeHtml(JSON.stringify(String(repoPath || '')));
                const hasChanges = !!(repo.status && repo.status.has_changes);
                const repoStatus = repo.status || {};
                let changeInfo = '';
                if (hasChanges) {
                    const changes = [];
                    if (repoStatus.modified) changes.push((repoStatus.modified || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.modified') : 'Modified'));
                    if (repoStatus.added) changes.push((repoStatus.added || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.added') : 'Added'));
                    if (repoStatus.deleted) changes.push((repoStatus.deleted || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.deleted') : 'Deleted'));
                    if (repoStatus.untracked) changes.push((repoStatus.untracked || 0) + ' ' + (typeof I18n !== 'undefined' ? I18n.t('git.status.untracked') : 'Untracked'));
                    changeInfo = changes.length ? ' · ' + changes.join(', ') : '';
                }

                const titleHtml = '<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">' +
                    '<span style="font-weight:600;word-break:break-all;font-size:15px;">' + escapeHtml(repoName) + '</span>' +
                    '<div class="git-link-group">' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoDiff(' + repoPathArg + ');">diff</button>' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoCheckout(' + repoPathArg + ');">checkout</button>' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoPull(' + repoPathArg + ');">pull</button>' +
                    '<button type="button" class="git-link-btn" onclick="__gitDoPush(' + repoPathArg + ');">push</button>' +
                    '</div>' +
                    '</div>';
                __gitSetDrawerTitle(titleHtml);

                const headerHtml = '<div style="padding:12px 12px 8px;"><div id="gitDiffFilesBox"></div></div>';
                const diffMeta = { has_changes: hasChanges, change_info: changeInfo };

                __gitMountCommitList(panelEl, repoPath, { html: headerHtml, branch: '' });
                __gitLoadDiffFileList(repoPath, diffMeta);
            }

            repos.forEach(function(r) {
                const opt = document.createElement('option');
                opt.value = String(r.id);
                opt.textContent = r.name + ' (' + (r.status && r.status.branch ? r.status.branch : '-') + ')';
                if (select) select.appendChild(opt);
            });

            if (select) {
                select.addEventListener('change', function() {
                    const rid = this.value;
                    const repo = repos.find(function(r) { return String(r.id) === String(rid); });
                    renderRepo(repo);
                });
            }

            if (repos.length > 0) renderRepo(repos[0]);
        })
        .catch(function() {
            if (!container) return;
            container.innerHTML = __gitErrorBannerHtml(typeof I18n !== 'undefined' ? I18n.t('common.load_failed') : 'Load failed');
            const btn = document.getElementById('gitRetryBtn');
            if (btn) btn.addEventListener('click', function() { window.loadGitList(); });
        });
};

// Git 自动刷新
window.__gitAutoRefreshInterval = null;
window.__gitAutoRefreshEnabled = false;
window.__gitCurrentRepoPath = null;

window.toggleGitAutoRefresh = function() {
    const btn = document.getElementById('gitRefreshBtn');
    const icon = document.getElementById('gitRefreshIcon');

    if (window.__gitAutoRefreshEnabled) {
        // 关闭自动刷新
        window.__gitAutoRefreshEnabled = false;
        if (window.__gitAutoRefreshInterval) {
            clearInterval(window.__gitAutoRefreshInterval);
            window.__gitAutoRefreshInterval = null;
        }
        if (btn) btn.classList.remove('git-spinning');
    } else {
        // 开启自动刷新
        window.__gitAutoRefreshEnabled = true;
        if (btn) btn.classList.add('git-spinning');
        // 立即刷新一次
        if (window.__gitCurrentRepoPath) {
            __gitRefreshDiffFiles(window.__gitCurrentRepoPath);
        }
        // 每 3 秒刷新
        window.__gitAutoRefreshInterval = setInterval(function() {
            if (window.__gitAutoRefreshEnabled && window.__gitCurrentRepoPath) {
                __gitRefreshDiffFiles(window.__gitCurrentRepoPath);
            }
        }, 3000);
    }
};

function __gitRefreshDiffFiles(repoPath) {
    if (!repoPath) return;
    fetch('/api/git/diff-numstat?path=' + encodeURIComponent(String(repoPath || '')), { headers: authHeaders() })
        .then(function(r) { return r.json(); })
        .then(function(resp) {
            const payload = apiData(resp);
            const el = document.getElementById('gitDiffFilesBox');
            if (!el || !payload) return;
            // 使用 API 返回的绝对路径
            const absRepoPath = payload.repoPath || repoPath;
            // 获取当前 meta
            const metaEl = document.getElementById('gitDiffMeta');
            const meta = metaEl ? JSON.parse(metaEl.textContent) : { has_changes: true };
            __gitRenderDiffFileTable(el, payload.files || [], absRepoPath, meta);
        })
        .catch(function() {});
}

window.closeGitModal = function() { 
    Drawer.close('gitModal'); 
    // 关闭自动刷新
    if (window.__gitAutoRefreshInterval) {
        clearInterval(window.__gitAutoRefreshInterval);
        window.__gitAutoRefreshInterval = null;
    }
    window.__gitAutoRefreshEnabled = false;
    window.__gitCurrentRepoPath = null;
    const btn = document.getElementById('gitRefreshBtn');
    if (btn) btn.classList.remove('git-spinning');
};

window.openGitModal = function() {
    Drawer.open('gitModal');
    window.loadGitList();
    // 默认开启自动刷新
    if (!window.__gitAutoRefreshEnabled) {
        window.toggleGitAutoRefresh();
    }
};

window.__gitCommitListTestApi = {
    commitShortId: __gitCommitShortId,
    truncate: __gitTruncate,
    commitUrl: __gitCommitUrl,
    prepareCommitNavigation: __gitPrepareCommitNavigation,
    nextActiveIndex: __gitNextActiveIndex,
    scrollKey: __gitScrollKey,
    saveScroll: __gitSaveScroll,
    loadScroll: __gitLoadScroll,
    emptyHtml: __gitEmptyHtml,
    errorBannerHtml: __gitErrorBannerHtml,
    commitItemHtml: __gitCommitItemHtml,
    dirtyLineHtml: __gitDirtyLineHtml
};

if (typeof module !== 'undefined' && module.exports) {
    module.exports = window.__gitCommitListTestApi;
}

if (window && typeof window.addEventListener === 'function') {
    window.addEventListener('pageshow', function() {
        const repoPath = __gitConsumeReturnFlag();
        if (!repoPath) return;
        try {
            Drawer.open('gitModal');
        } catch (e) {
            return;
        }
        try {
            window.loadGitList(repoPath);
        } catch (e) {
            return;
        }
    });
}
