T4_Three_Ways / app.js
ahmedyradwan02's picture
Upload 44 files
105dd65 verified
Raw History Blame Contribute Delete
6.42 kB
// The three annotators, side by side: one card per clip, read from data.js (window.TRIAL). View only.
const rows = window.TRIAL || [];
const esc = (s) => String(s ?? "").replace(/[&<>"]/g, (c) => ({"&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;"}[c]));
const store = {
get(k) { try { return localStorage.getItem(k); } catch { return null; } },
set(k, v) { try { localStorage.setItem(k, v); } catch {} },
};
const GROUPS = [
["all", "All", () => true],
["tell", "Known tells", (r) => r.why.startsWith("tell")],
["looks", "Looks real", (r) => r.why.startsWith("looks real")],
["signs", "With signs", (r) => r.why.startsWith("signs")],
["cell", "One per cell", (r) => r.why.startsWith("cell")],
];
const GENERATOR = {facefusion: "FaceFusion", animate: "Animate", s2v: "S2V", real: "Real recording"};
const WHY = {tell: "Known tell (a reviewer's note)", "looks real": "Looks real (a reviewer's note)", signs: "Picked for its signs",
cell: "One per label and generator", sham: "Sham: a real clip, Gemini told it is fake"};
function text(list, isReal) {
if (!list || !list.length) return '<p class="real">—</p>';
const body = list.map((s) => `<span class="sent" data-claims="${(s.claims || []).join(",")}">${esc(s.text)}</span>`).join(" ");
return `<p class="${isReal ? "real" : ""}">${body}</p>`;
}
function arm(cls, title, note, r, sentences, claims, ok, extra = "") {
const body = ok === false ? '<p class="failed">Did not pass the checks after 3 tries.</p>' :
["video", "audio"].map((p) => `<div class="part"><div class="tag">${p}</div>${text(sentences[p], !r.fake_parts.includes(p))}</div>`).join("");
const facts = (claims || []).length ? `<div class="sub detail"><div class="label">Written from</div><ul>${claims.map((c) =>
`<li data-id="${c.id}"><b>${c.id}</b>${esc(c.text)}</li>`).join("")}</ul></div>` : "";
return `<section class="arm"><h3><span class="dot ${cls}"></span>${title}<small>${note}</small></h3>${body}${facts}${extra}</section>`;
}
function observed(o) {
if (!o || !o.agreed.length) return "";
const items = o.agreed.map((x) => `<li class="${x.kept ? "kept" : "dropped"}">${esc(x.when)} · ${esc(x.where)} · ${esc(x.what)}</li>`).join("");
return `<div class="sub detail"><div class="label">What Gemini saw</div>
<p style="margin:0 0 4px">Seen in at least 2 of 3 looks. ✓ confirmed twice on the clip alone; ✕ not confirmed.</p><ul>${items}</ul></div>`;
}
function header(r) {
const parts = ["video", "audio"].map((p) => {
const fake = !r.sham && r.fake_parts.includes(p);
return `<span class="${fake ? "fake" : "real"}">${p[0].toUpperCase() + p.slice(1)} ${fake ? "fake" : "real"}</span>`;
}).join(" · ");
const kind = Object.keys(WHY).find((k) => r.why.startsWith(k));
const note = kind === "tell" ? r.why.split(": ").slice(1).join(": ") : ""; // the reviewer's words
const rowsDl = [["Picked", WHY[kind] || r.why]];
if (note) rowsDl.push(["Note", note]);
if (r.signs.length) rowsDl.push(["Signs", r.signs.map((s) => `${esc(s.artifact)} <span style="color:var(--faint)">(${s.score} ${s.value})</span>`).join("<br>")]);
const ref = r.reference ? `<button class="link" data-ref>Show the real clip</button>
<div class="ref" hidden><video controls preload="none" src="${esc(r.reference)}"></video></div>` : "";
return `<div class="top"><div class="media"><video controls preload="metadata" src="${esc(r.clip)}"></video></div>
<div class="info"><h2>Clip ${r.n}</h2><div class="meta">${GENERATOR[r.generator] || r.generator} · ${parts}</div>
<dl class="facts">${rowsDl.map(([k, v]) => `<dt>${k}</dt><dd>${k === "Signs" ? v : esc(v)}</dd>`).join("")}</dl>${ref}</div></div>`;
}
function card(r) {
if (r.sham) {
const kept = (r.observed?.agreed || []).filter((x) => x.kept)
.map((x) => `<li>${esc(x.when)} · ${esc(x.where)} · ${esc(x.what)}</li>`).join("");
return `<article class="clip">${header(r)}<div class="cols" style="grid-template-columns:1fr"><section class="arm sham">
<h3><span class="dot G"></span>Gemini<small>what it kept on a real clip</small></h3>
<ul class="found">${kept || '<li class="none">Nothing kept.</li>'}</ul>
<p class="note">This clip is a real recording, so anything kept here is a hallucination.</p></section></div></article>`;
}
return `<article class="clip">${header(r)}<div class="cols">
${arm("S", "Systematic", "metadata", r, r.S, [], true)}
${arm("P", "Claude", "text only", r, r.P, r.P_claims, r.P_ok)}
${arm("G", "Gemini", "sees and hears the clip", r, r.G, r.G_claims, r.G_ok, observed(r.observed))}
</div></article>`;
}
function show(key) {
const test = GROUPS.find((g) => g[0] === key)[2];
document.getElementById("clips").innerHTML = rows.filter(test).map(card).join("");
for (const b of document.querySelectorAll("#filters button")) b.classList.toggle("on", b.dataset.key === key);
store.set("t4three:filter", key);
}
document.getElementById("filters").innerHTML = GROUPS.map(([k, label, test]) =>
`<button data-key="${k}">${label}<span class="count">${rows.filter(test).length}</span></button>`).join("");
document.getElementById("filters").onclick = (e) => { const b = e.target.closest("button"); if (b) show(b.dataset.key); };
const details = document.getElementById("details");
details.checked = store.get("t4three:details") === "1";
document.body.classList.toggle("details", details.checked);
details.onchange = () => { document.body.classList.toggle("details", details.checked); store.set("t4three:details", details.checked ? "1" : "0"); };
document.addEventListener("click", (e) => {
const b = e.target.closest("[data-ref]");
if (!b) return;
const ref = b.nextElementSibling;
ref.hidden = !ref.hidden;
b.textContent = ref.hidden ? "Show the real clip" : "Hide the real clip";
});
// hovering a sentence highlights it and, with Details on, the facts it is written from
document.addEventListener("mouseover", (e) => {
const s = e.target.closest(".sent");
document.querySelectorAll(".hl").forEach((x) => x.classList.remove("hl"));
if (!s || !s.dataset.claims) return;
s.classList.add("hl");
const col = s.closest(".arm");
for (const id of s.dataset.claims.split(",")) col.querySelector(`.sub li[data-id="${id}"]`)?.classList.add("hl");
});
const start = store.get("t4three:filter");
show(GROUPS.some((g) => g[0] === start) ? start : "all");