// — detail panel for the repository selected in . // // A self-contained control (AGENT.md §1.1): shadow DOM, fetches its own data, // cleans up on disconnect. It has no reference to ; it listens on the // document for the bubbling/composed `repo:select` event (the sanctioned // cross-component channel) and fetches `/api/repo?path=…` for the chosen repo. class RepoDetail extends HTMLElement { #controller = null; #onSelect = null; constructor() { super(); this.attachShadow({ mode: 'open' }); } connectedCallback() { this.#renderShell(); this.#renderEmpty(); this.#onSelect = (e) => this.#load(e.detail?.path); document.addEventListener('repo:select', this.#onSelect); } disconnectedCallback() { document.removeEventListener('repo:select', this.#onSelect); this.#controller?.abort(); } async #load(path) { if (!path) return; this.#controller?.abort(); this.#controller = new AbortController(); this.#body().innerHTML = `

Loading…

`; try { const res = await fetch(`/api/repo?path=${encodeURIComponent(path)}`, { signal: this.#controller.signal, }); if (!res.ok) throw new Error(`HTTP ${res.status}`); this.#renderDetail(await res.json()); } catch (err) { if (err.name !== 'AbortError') { this.#body().innerHTML = `

Could not load details: ${this.#esc(err.message)}

`; } } } #body() { return this.shadowRoot.getElementById('body'); } #renderEmpty() { this.#body().innerHTML = `

Select a repository to see its details.

`; } #renderDetail(r) { const remotes = (r.remoteDetails || []).map((rm) => `
  • ${this.#esc(rm.name)} ${this.#esc(rm.url)}
  • ` ).join('') || `
  • none
  • `; const branches = (r.branches || []).map((b) => `
  • ${b.current ? '' : ''}${this.#esc(b.name)} ${b.upstream ? `→ ${this.#esc(b.upstream)}` : ''}
  • ` ).join('') || `
  • none
  • `; const commits = (r.commits || []).map((c) => `
  • ${this.#esc(c.short)} ${this.#esc(c.subject)} ${this.#esc(c.author)} · ${this.#esc(c.date)}
  • ` ).join('') || `
  • none
  • `; this.#body().innerHTML = `

    ${this.#esc(r.name)}

    ${r.dirty ? 'dirty' : 'clean'} ${r.ahead ? `↑${r.ahead}` : ''} ${r.behind ? `↓${r.behind}` : ''}

    ${this.#esc(r.path)}

    on ${this.#esc(r.branch || '—')}

    ${r.error ? `

    ${this.#esc(r.error)}

    ` : ''}

    Remotes

      ${remotes}

    Branches

      ${branches}

    Recent commits

      ${commits}
    `; } #renderShell() { this.shadowRoot.innerHTML = `
    `; } #esc(s) { return String(s ?? '').replace(/[&<>"']/g, (c) => ( { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c] )); } } customElements.define('repo-detail', RepoDetail);