Menu
Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f
AkurAI-Framework / site/frontend/ai/components/display/citation.js
Latest change 88d669a6b3c5464f8a947a66be0437a90424470f - feat(ai): add Display components — citation, link_preview, stats_display, terminal, weather_widget by Ólafur Búi Ólafsson
/**
* Citation — compact cited-source card.
* Payload: { title, url, snippet, favicon? }
*/
function esc(str) {
const d = document.createElement("div");
d.textContent = String(str ?? "");
return d.innerHTML;
}
function el(tag, cls, children) {
const node = document.createElement(tag);
if (cls) node.className = cls;
if (Array.isArray(children)) children.forEach(c => c && node.appendChild(c));
return node;
}
function txt(content) {
return document.createTextNode(content);
}
export const def = {
name: "citation",
category: "Display",
title: "Citation",
match(payload) {
return (
payload !== null &&
typeof payload === "object" &&
typeof payload.title === "string" &&
typeof payload.url === "string" &&
typeof payload.snippet === "string"
);
},
render(payload) {
const { title, url, snippet, favicon } = payload;
// Derive display domain from url (best-effort, no throws)
let domain = "";
try { domain = new URL(url).hostname.replace(/^www\./, ""); } catch (_) { domain = url; }
const card = el("div", "card tc-display-citation");
// Header row: favicon + domain + external link icon
const header = el("div", "tc-display-citation__header");
if (favicon) {
const img = document.createElement("img");
img.className = "tc-display-citation__favicon";
img.width = 16;
img.height = 16;
// favicon is a URL — XSS: we only set src, not innerHTML
img.src = favicon;
img.alt = "";
header.appendChild(img);
} else {
// Fallback: first letter of domain as small badge
const fb = el("span", "tc-display-citation__favicon-fallback");
fb.textContent = domain.charAt(0).toUpperCase();
header.appendChild(fb);
}
const domainSpan = el("span", "tc-display-citation__domain muted");
domainSpan.textContent = domain;
header.appendChild(domainSpan);
card.appendChild(header);
// Title as a link
const titleLink = document.createElement("a");
titleLink.className = "tc-display-citation__title";
titleLink.href = url;
titleLink.rel = "noopener noreferrer";
titleLink.target = "_blank";
titleLink.textContent = title;
card.appendChild(titleLink);
// Snippet
const snip = el("p", "tc-display-citation__snippet muted");
snip.textContent = snippet;
card.appendChild(snip);
return card;
},
example: {
title: "From-scratch SQL engine — AkurAI-Framework",
url: "https://github.com/olibuijr/AkurAI-Framework",
snippet:
"A pure-Rust zero-runtime-dep full-stack framework with a built-in SQL engine, native ESM frontend, and semantic search.",
favicon: "https://github.com/favicon.ico",
},
};