import { templated } from 'atlibs/component/index.js' import { channelView } from './socialweb-cubit.js' const Base = await templated(new URL('./swc-channel.html', import.meta.url)); const clone = (id) => Base.templates[id].content.cloneNode(true); const time = (ts) => (ts ? new Date(ts).toLocaleTimeString() : ""); // The atirc channel as agents see it: who is talking, owner questions, VM requests from the // account's agents (Approve emits approve {seq}), and every line tagged by kind. All channel // text goes in through textContent. Renders {ui, atirc}. customElements.define('swc-channel', class extends Base { #view; render({ ui, atirc }, previous) { this.querySelector(".panel").style.display = atirc.session === "signed-in" ? "block" : "none"; this.querySelector(".channel").textContent = atirc.channel; const { status, list, error } = atirc.messages; this.querySelector(".lines").toggleAttribute("aria-busy", status === "loading" || status === "idle"); this.querySelector(".error").textContent = status === "error" ? `Error reading channel: ${error}` : ""; if (!this.#view || previous?.atirc.messages.list !== list || previous?.ui.did !== ui.did || previous?.atirc.channel !== atirc.channel) { this.#view = channelView(list, { did: ui.did, handle: atirc.handle, channel: atirc.channel }); this.#lines(this.#view); } this.#requests(this.#view.requests, ui); } #lines({ lines, agents, questions }) { this.querySelector(".agents").replaceChildren(...agents.map((a) => { const li = clone("agent"); li.querySelector(".nick").textContent = a.nick; li.querySelector(".nick").title = a.identity; li.querySelector(".meta").textContent = `${a.lines} lines, ${a.scrobbles} scrobbles, last ${time(a.last)}`; return li; })); this.querySelector(".questions").replaceChildren(...questions.map((q) => { const li = clone("question"); li.querySelector(".status").textContent = q.status; li.querySelector(".question").textContent = `${q.question} (${q.askedBy})`; li.querySelector(".outcome").textContent = q.outcome ? `${q.status} by ${q.by}: ${q.outcome}` : ""; return li; })); this.querySelector(".lines").replaceChildren(...lines.map((m) => { const li = clone("line"); li.firstElementChild.dataset.kind = m.kind; li.querySelector(".ts").textContent = time(m.ts); li.querySelector(".nick").textContent = m.nick; li.querySelector(".nick").title = m.identity ?? ""; if (m.unverified) li.firstElementChild.dataset.unverified = ""; li.querySelector(".kind").textContent = m.unverified ? "unverified" : m.kind === "chat" ? "" : m.kind; li.querySelector(".text").textContent = m.text; return li; })); } #requests(requests, ui) { const allowed = ui.features.includes("agents") && ui.allowAgentRequests; this.querySelector(".requests").replaceChildren(...requests.map((r) => { const li = clone("request"); const approval = ui.approvals[r.seq]; li.querySelector(".who").textContent = `#${r.seq} ${r.identity}`; li.querySelector(".input").textContent = JSON.stringify(r.input); const button = li.querySelector(".approve"); button.disabled = !allowed || r.replied || Boolean(approval); button.toggleAttribute("aria-busy", approval?.status === "running"); button.onclick = () => this.dispatchEvent(new CustomEvent("approve", { bubbles: true, detail: { seq: r.seq } })); li.querySelector(".approval").textContent = approval?.error ?? (approval?.result ? `rfp ${approval.result.rfp?.uri}` : r.replied ? "answered" : ""); return li; })); } });