~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-socialweb-computer-requester

socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons

0
0
main
71 lines · 3.5 KB Raw History
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;
		}));
	}
});