2026-10-07 13:09:32 -04:00
|
|
|
// Repo / CI panel: git state of the current session's folder plus the Gitea CI
|
|
|
|
|
// status of its HEAD commit (`devdesk attach --repo <session>`).
|
|
|
|
|
|
|
|
|
|
const CI = {
|
|
|
|
|
success: ['passing', 'lt-status-completed'],
|
|
|
|
|
failure: ['failed', 'lt-status-failed'],
|
|
|
|
|
error: ['error', 'lt-status-failed'],
|
|
|
|
|
pending: ['running', 'lt-status-running'],
|
|
|
|
|
warning: ['warning', 'lt-status-pending'],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function link(href, text) {
|
|
|
|
|
const a = document.createElement('a');
|
|
|
|
|
a.href = href;
|
|
|
|
|
a.target = '_blank';
|
|
|
|
|
a.rel = 'noopener';
|
|
|
|
|
a.textContent = text;
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function renderRepo(root, repo) {
|
|
|
|
|
const field = (name) => root.querySelector(`[data-field="${name}"]`);
|
|
|
|
|
const set = (name, value) => field(name).replaceChildren(value);
|
2026-10-07 23:34:59 -04:00
|
|
|
const grid = root.querySelector('.dd-repo-grid');
|
2026-10-07 13:09:32 -04:00
|
|
|
const note = root.querySelector('.dd-repo-note');
|
2026-10-07 23:34:59 -04:00
|
|
|
const fix = root.querySelector('.dd-fix-ci');
|
|
|
|
|
fix.hidden = true;
|
2026-10-07 13:09:32 -04:00
|
|
|
|
2026-10-07 23:34:59 -04:00
|
|
|
// No repository: one line instead of a grid of dashes (devdesk#17).
|
2026-10-07 13:09:32 -04:00
|
|
|
if (!repo || !repo.git) {
|
|
|
|
|
root.dataset.state = 'empty';
|
2026-10-07 23:34:59 -04:00
|
|
|
grid.hidden = true;
|
|
|
|
|
note.textContent = repo ? `Not a git repository: ${repo.dir}` : 'Open a session to see its repository.';
|
2026-10-07 13:09:32 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root.dataset.state = 'live';
|
2026-10-07 23:34:59 -04:00
|
|
|
grid.hidden = false;
|
2026-10-07 13:09:32 -04:00
|
|
|
set('repo', repo.url ? link(repo.url, repo.repo) : (repo.remote || 'no remote'));
|
|
|
|
|
const sync = repo.upstream
|
|
|
|
|
? [repo.ahead ? `↑${repo.ahead}` : '', repo.behind ? `↓${repo.behind}` : ''].filter(Boolean).join(' ') || 'in sync'
|
|
|
|
|
: 'no upstream';
|
|
|
|
|
set('branch', `${repo.branch} @ ${repo.sha || '—'} · ${sync}`);
|
|
|
|
|
set('dirty', repo.dirty ? `${repo.dirty} changed` : 'clean');
|
|
|
|
|
|
|
|
|
|
const ci = field('ci');
|
|
|
|
|
const [label, cls] = repo.ci?.total ? (CI[repo.ci.state] || [repo.ci.state, 'lt-status-pending'])
|
2026-10-07 23:34:59 -04:00
|
|
|
: [repo.ahead ? 'not pushed' : 'no CI', ''];
|
2026-10-07 13:09:32 -04:00
|
|
|
ci.className = `lt-status ${cls}`;
|
|
|
|
|
ci.replaceChildren(repo.ci?.url ? link(repo.ci.url, label) : label);
|
|
|
|
|
|
2026-10-07 23:34:59 -04:00
|
|
|
set('prs', repo.url && repo.prs != null ? link(`${repo.url}/pulls`, `${repo.prs} open`) : '—');
|
2026-10-07 13:09:32 -04:00
|
|
|
note.textContent = repo.repo ? '' : 'Not a Gitea repository; CI status unavailable.';
|
2026-10-07 23:34:59 -04:00
|
|
|
|
|
|
|
|
if (repo.ci?.total && ['failure', 'error'].includes(repo.ci.state)) {
|
|
|
|
|
fix.hidden = false;
|
|
|
|
|
fix.dataset.askClaude = `CI is failing for ${repo.repo} on ${repo.branch} at ${repo.sha}`
|
|
|
|
|
+ ` (${repo.ci.url || 'see Gitea Actions'}). Use the gitea MCP server to read the failed job's log,`
|
|
|
|
|
+ ' find the cause, and fix it. Tell me the plan before pushing anything.';
|
|
|
|
|
}
|
2026-10-07 13:09:32 -04:00
|
|
|
}
|