export function createImetaVariants(variants, utils) { const { createListEmpty, createPlaceholder, createLinkMarkup } = utils; if (!Array.isArray(variants) || variants.length === 0) { return createListEmpty("No media variants provided."); } const ol = document.createElement("ol"); ol.className = "space-y-3"; variants.forEach((variant, index) => { if (!variant) return; const li = document.createElement("li"); li.className = "rounded-md border border-overlay bg-overlay-muted p-3 space-y-3"; // Header const headerDiv = document.createElement("div"); headerDiv.className = "flex flex-wrap items-center justify-between gap-2 text-xs text-muted"; const variantLabel = document.createElement("span"); variantLabel.className = "font-semibold text-primary"; variantLabel.textContent = `Variant ${index + 1}`; headerDiv.appendChild(variantLabel); if (variant.autoGenerated) { const badgesDiv = document.createElement("div"); badgesDiv.className = "flex flex-wrap gap-2"; const badge = document.createElement("span"); badge.className = "badge"; badge.dataset.variant = "warning"; badge.textContent = "Auto-generated"; badgesDiv.appendChild(badge); headerDiv.appendChild(badgesDiv); } li.appendChild(headerDiv); // DL Grid const dl = document.createElement("dl"); dl.className = "grid gap-3 sm:grid-cols-2 text-xs text-subtle"; const createDtDd = (label, contentNode, colSpan = false) => { const div = document.createElement("div"); if (colSpan) div.className = "sm:col-span-2"; const dt = document.createElement("dt"); dt.className = "font-semibold text-primary"; dt.textContent = label; div.appendChild(dt); const dd = document.createElement("dd"); dd.className = "mt-1"; dd.appendChild(contentNode); div.appendChild(dd); return div; }; // Mime const mimeNode = variant.m ? (() => { const c = document.createElement("code"); c.className = "rounded bg-overlay-panel-soft px-1.5 py-0.5 text-primary"; c.textContent = variant.m; return c; })() : createPlaceholder(); dl.appendChild(createDtDd("MIME type", mimeNode)); // Dim const dimNode = variant.dim ? (() => { const c = document.createElement("code"); c.className = "rounded bg-overlay-panel-soft px-1.5 py-0.5 text-primary"; c.textContent = variant.dim; return c; })() : createPlaceholder(); dl.appendChild(createDtDd("Dimensions", dimNode)); // URL const urlNode = variant.url ? createLinkMarkup(variant.url) : createPlaceholder("No URL"); dl.appendChild(createDtDd("URL", urlNode, true)); // Hash const hashNode = variant.x ? (() => { const c = document.createElement("code"); c.className = "break-all rounded bg-overlay-panel-soft px-1.5 py-0.5 text-primary"; c.textContent = variant.x; return c; })() : createPlaceholder("Not provided"); dl.appendChild(createDtDd("Content hash", hashNode, true)); li.appendChild(dl); // Nested lists const nestedGrid = document.createElement("div"); nestedGrid.className = "grid gap-3 sm:grid-cols-3 text-xs text-subtle"; const createNestedList = (label, values, emptyLabel) => { const container = document.createElement("div"); const p = document.createElement("p"); p.className = "text-2xs uppercase tracking-wide text-subtle"; p.textContent = label; container.appendChild(p); if (!Array.isArray(values) || values.length === 0) { container.appendChild(createListEmpty(emptyLabel)); return container; } const validItems = values .map(v => createLinkMarkup(v)) .filter(node => node.textContent !== ""); if (validItems.length === 0) { container.appendChild(createListEmpty(emptyLabel)); return container; } const ul = document.createElement("ul"); ul.className = "mt-1 space-y-1 text-xs text-primary"; validItems.forEach(itemNode => { const itemLi = document.createElement("li"); itemLi.appendChild(itemNode); ul.appendChild(itemLi); }); container.appendChild(ul); return container; }; nestedGrid.appendChild(createNestedList("Images", variant.image, "No image URLs.")); nestedGrid.appendChild(createNestedList("Fallbacks", variant.fallback, "No fallback URLs.")); nestedGrid.appendChild(createNestedList("Services", variant.service, "No service hints.")); li.appendChild(nestedGrid); ol.appendChild(li); }); return ol; } export function createTextTracks(tracks, utils) { const { createListEmpty, createPlaceholder, createLinkMarkup } = utils; if (!Array.isArray(tracks) || tracks.length === 0) { return createListEmpty("No caption tracks."); } const ol = document.createElement("ol"); ol.className = "space-y-3"; tracks.forEach((track, index) => { const li = document.createElement("li"); li.className = "rounded-md border border-overlay bg-overlay-muted p-3 space-y-3"; const headerDiv = document.createElement("div"); headerDiv.className = "flex items-center justify-between text-xs text-muted"; const label = document.createElement("span"); label.className = "font-semibold text-primary"; label.textContent = `Track ${index + 1}`; headerDiv.appendChild(label); if (track.language) { const langSpan = document.createElement("span"); langSpan.className = "pill"; langSpan.dataset.size = "compact"; langSpan.dataset.variant = "neutral"; langSpan.textContent = track.language; headerDiv.appendChild(langSpan); } li.appendChild(headerDiv); const dl = document.createElement("dl"); dl.className = "grid gap-3 sm:grid-cols-2 text-xs text-subtle"; const createDtDd = (label, contentNode, colSpan = false) => { const div = document.createElement("div"); if (colSpan) div.className = "sm:col-span-2"; const dt = document.createElement("dt"); dt.className = "font-semibold text-primary"; dt.textContent = label; div.appendChild(dt); const dd = document.createElement("dd"); dd.className = "mt-1"; dd.appendChild(contentNode); div.appendChild(dd); return div; }; const urlNode = track.url ? createLinkMarkup(track.url) : createPlaceholder("No URL"); dl.appendChild(createDtDd("URL", urlNode, true)); const typeNode = track.type ? (() => { const c = document.createElement("code"); c.className = "rounded bg-overlay-panel-soft px-1.5 py-0.5 text-primary"; c.textContent = track.type; return c; })() : createPlaceholder(); dl.appendChild(createDtDd("Type", typeNode)); const langNode = track.language ? (() => { const s = document.createElement("span"); s.className = "pill"; s.dataset.size = "compact"; s.dataset.variant = "neutral"; s.textContent = track.language; return s; })() : createPlaceholder(); dl.appendChild(createDtDd("Language", langNode)); li.appendChild(dl); ol.appendChild(li); }); return ol; } export function createSegments(segments, utils) { const { createListEmpty, createPlaceholder, createLinkMarkup, formatDurationSeconds } = utils; if (!Array.isArray(segments) || segments.length === 0) { return createListEmpty("No chapter segments."); } const ol = document.createElement("ol"); ol.className = "space-y-3"; const createTimeNode = (seconds, raw) => { if (Number.isFinite(seconds)) { const display = formatDurationSeconds(seconds); const suffix = seconds === 1 ? "second" : "seconds"; const frag = document.createDocumentFragment(); const span1 = document.createElement("span"); span1.className = "text-primary"; span1.textContent = display; frag.appendChild(span1); const space = document.createTextNode(" "); frag.appendChild(space); const span2 = document.createElement("span"); span2.className = "text-subtle"; span2.textContent = `(${seconds} ${suffix})`; frag.appendChild(span2); return frag; } if (raw) { const span = document.createElement("span"); span.className = "text-primary"; span.textContent = raw; return span; } return createPlaceholder(); }; segments.forEach((segment, index) => { const li = document.createElement("li"); li.className = "rounded-md border border-overlay bg-overlay-muted p-3 space-y-3"; const headerDiv = document.createElement("div"); headerDiv.className = "text-xs font-semibold text-primary"; headerDiv.textContent = `Chapter ${index + 1}`; li.appendChild(headerDiv); const dl = document.createElement("dl"); dl.className = "grid gap-3 sm:grid-cols-2 text-xs text-subtle"; const createDtDd = (label, contentNode, colSpan = false) => { const div = document.createElement("div"); if (colSpan) div.className = "sm:col-span-2"; const dt = document.createElement("dt"); dt.className = "font-semibold text-primary"; dt.textContent = label; div.appendChild(dt); const dd = document.createElement("dd"); dd.className = "mt-1"; dd.appendChild(contentNode); div.appendChild(dd); return div; }; dl.appendChild(createDtDd("Start", createTimeNode(segment.startSeconds, segment.startRaw))); dl.appendChild(createDtDd("End", createTimeNode(segment.endSeconds, segment.endRaw))); const titleNode = segment.title ? (() => { const p = document.createElement("p"); p.className = "whitespace-pre-wrap text-primary"; p.textContent = segment.title; return p; })() : createPlaceholder("No title"); dl.appendChild(createDtDd("Title", titleNode, true)); const thumbNode = segment.thumbnail ? createLinkMarkup(segment.thumbnail) : createPlaceholder("No thumbnail"); dl.appendChild(createDtDd("Thumbnail", thumbNode, true)); li.appendChild(dl); ol.appendChild(li); }); return ol; } export function createHashtags(hashtags, utils) { const { createListEmpty } = utils; if (!Array.isArray(hashtags) || hashtags.length === 0) { return createListEmpty("No hashtags provided."); } const div = document.createElement("div"); div.className = "flex flex-wrap gap-2"; hashtags.forEach(tag => { const label = tag.startsWith("#") ? tag : `#${tag}`; const span = document.createElement("span"); span.className = "pill"; span.dataset.size = "compact"; span.dataset.variant = "neutral"; span.textContent = label; div.appendChild(span); }); return div; } export function createParticipants(participants, utils) { const { createListEmpty, createPlaceholder, createLinkMarkup, truncateMiddle } = utils; if (!Array.isArray(participants) || participants.length === 0) { return createListEmpty("No participants listed."); } const ol = document.createElement("ol"); ol.className = "space-y-3"; participants.forEach((participant, index) => { const li = document.createElement("li"); li.className = "rounded-md border border-overlay bg-overlay-muted p-3 space-y-2"; const headerDiv = document.createElement("div"); headerDiv.className = "text-xs font-semibold text-primary"; headerDiv.textContent = `Participant ${index + 1}`; li.appendChild(headerDiv); const dl = document.createElement("dl"); dl.className = "grid gap-3 sm:grid-cols-2 text-xs text-subtle"; const createDtDd = (label, contentNode, colSpan = false) => { const div = document.createElement("div"); if (colSpan) div.className = "sm:col-span-2"; const dt = document.createElement("dt"); dt.className = "font-semibold text-primary"; dt.textContent = label; div.appendChild(dt); const dd = document.createElement("dd"); dd.className = "mt-1"; dd.appendChild(contentNode); div.appendChild(dd); return div; }; const displayPubkey = participant.pubkey ? truncateMiddle ? truncateMiddle(participant.pubkey, 48) : participant.pubkey : ""; const pubkeyNode = participant.pubkey ? (() => { const c = document.createElement("code"); c.className = "break-all rounded bg-overlay-panel-soft px-1.5 py-0.5 text-primary"; c.title = participant.pubkey; c.textContent = displayPubkey; return c; })() : createPlaceholder("No pubkey"); dl.appendChild(createDtDd("Pubkey", pubkeyNode, true)); const relayNode = participant.relay ? createLinkMarkup(participant.relay, { breakAll: false }) : createPlaceholder("No relay specified"); dl.appendChild(createDtDd("Relay", relayNode, true)); li.appendChild(dl); ol.appendChild(li); }); return ol; } export function createReferences(references, utils) { const { createListEmpty, createLinkMarkup } = utils; if (!Array.isArray(references) || references.length === 0) { return createListEmpty("No external references."); } const items = references .map((entry) => createLinkMarkup(entry)) .filter(node => node.textContent !== ""); if (items.length === 0) { return createListEmpty("No external references."); } const ul = document.createElement("ul"); ul.className = "space-y-1 text-xs text-primary"; items.forEach(node => { const li = document.createElement("li"); li.appendChild(node); ul.appendChild(li); }); return ul; }