trace-reports / index.html
merve's picture
merve HF Staff
Update index.html
97bc9b2 verified
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Trace Personality Bulletin</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=IBM+Plex+Mono:wght@400;500;600&family=Alfa+Slab+One&display=swap"
rel="stylesheet"
/>
<style>
:root {
--ink: #353e60;
--ink-soft: rgba(53, 62, 96, 0.7);
--ink-faint: rgba(53, 62, 96, 0.18);
--surface: #fff8de;
--surface-deep: #f6eecf;
--accent: #00704a;
--accent2: #ff9d00;
--red: #db3328;
--yellow: #ffd21e;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
background: var(--surface);
color: var(--ink);
font-family: 'Source Sans 3', -apple-system, system-ui, sans-serif;
min-height: 100vh;
}
/* Faint dotted texture across the whole page */
body {
background-image:
radial-gradient(var(--ink-faint) 1px, transparent 1.4px);
background-size: 22px 22px;
}
.page {
max-width: 880px;
margin: 0 auto;
padding: 48px 24px 80px;
}
/* ---- Input panel ("retro newsprint card") ---- */
.panel {
position: relative;
background: var(--surface);
color: var(--ink);
padding: 44px 56px 40px;
box-shadow:
0 24px 60px rgba(0, 0, 0, 0.18),
0 6px 16px rgba(0, 0, 0, 0.10);
border-radius: 6px;
isolation: isolate;
}
.panel::before,
.panel::after {
content: "";
position: absolute;
pointer-events: none;
}
.panel::before {
inset: 14px;
border: 2px solid var(--ink);
border-radius: 3px;
}
.panel::after {
inset: 22px;
border: 1px solid var(--ink);
border-radius: 2px;
}
.corner {
position: absolute;
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
background: var(--surface);
color: var(--ink);
font-size: 14px;
font-weight: 700;
z-index: 2;
}
.corner.tl { top: 6px; left: 6px; }
.corner.tr { top: 6px; right: 6px; }
.corner.bl { bottom: 6px; left: 6px; }
.corner.br { bottom: 6px; right: 6px; }
.masthead {
text-align: center;
position: relative;
}
.masthead .tiny {
font-family: 'IBM Plex Mono', monospace;
font-size: 11px;
letter-spacing: 0.34em;
text-transform: uppercase;
color: var(--ink-soft);
margin-bottom: 10px;
}
.masthead h1 {
font-family: 'Alfa Slab One', serif;
font-weight: 400;
font-size: clamp(28px, 5vw, 40px);
letter-spacing: 0.04em;
line-height: 1;
text-transform: uppercase;
color: var(--ink);
margin: 0;
}
.masthead h1 .accent {
color: var(--accent);
text-shadow: 3px 3px 0 var(--ink), -2px -2px 0 var(--accent2);
}
.rule {
display: flex;
align-items: center;
gap: 12px;
margin: 14px auto 0;
max-width: 540px;
color: var(--ink-soft);
font-family: 'IBM Plex Mono', monospace;
font-size: 12px;
letter-spacing: 0.32em;
}
.rule .line {
flex: 1;
height: 1px;
background: currentColor;
opacity: 0.5;
}
.lede {
margin: 18px auto 0;
text-align: center;
max-width: 580px;
font-style: italic;
font-size: 16px;
line-height: 1.45;
color: var(--ink-soft);
}
/* ---- Form ---- */
.form {
margin-top: 34px;
display: flex;
flex-direction: column;
gap: 26px;
}
.field-label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
font-family: 'IBM Plex Mono', monospace;
font-size: 11px;
letter-spacing: 0.28em;
text-transform: uppercase;
color: var(--ink);
margin-bottom: 10px;
}
.field-label .value {
font-family: 'Alfa Slab One', serif;
font-size: 22px;
line-height: 1;
letter-spacing: 0.02em;
color: var(--accent);
text-shadow: 2px 2px 0 var(--ink);
}
/* Text input — looks like a typewritten form line */
.repo-input {
width: 100%;
background: var(--surface-deep);
color: var(--ink);
border: 1.5px solid var(--ink);
border-radius: 4px;
padding: 14px 16px;
font-family: 'IBM Plex Mono', monospace;
font-size: 16px;
letter-spacing: 0.02em;
box-shadow: 4px 4px 0 var(--ink);
transition: transform 80ms ease, box-shadow 80ms ease;
}
.repo-input::placeholder {
color: var(--ink-soft);
font-style: italic;
}
.repo-input:focus {
outline: none;
transform: translate(-1px, -1px);
box-shadow: 5px 5px 0 var(--accent2);
}
/* Slider — custom retro track + dial */
.slider-wrap { padding: 4px 4px 6px; }
.slider {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 22px;
background: transparent;
margin: 0;
cursor: pointer;
}
.slider:focus { outline: none; }
.slider::-webkit-slider-runnable-track {
height: 6px;
background:
linear-gradient(to right, var(--accent2) 0%, var(--accent2) var(--pct, 60%), var(--ink-faint) var(--pct, 60%), var(--ink-faint) 100%);
border: 1.5px solid var(--ink);
border-radius: 999px;
}
.slider::-moz-range-track {
height: 6px;
background: var(--ink-faint);
border: 1.5px solid var(--ink);
border-radius: 999px;
}
.slider::-moz-range-progress {
height: 6px;
background: var(--accent2);
border-radius: 999px 0 0 999px;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--surface);
border: 2px solid var(--ink);
box-shadow: 2px 2px 0 var(--ink);
margin-top: -10px;
cursor: grab;
}
.slider::-webkit-slider-thumb:active { cursor: grabbing; transform: translate(1px, 1px); box-shadow: 1px 1px 0 var(--ink); }
.slider::-moz-range-thumb {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--surface);
border: 2px solid var(--ink);
box-shadow: 2px 2px 0 var(--ink);
cursor: grab;
}
.slider-ticks {
display: flex;
justify-content: space-between;
margin-top: 6px;
font-family: 'IBM Plex Mono', monospace;
font-size: 10px;
letter-spacing: 0.18em;
color: var(--ink-soft);
}
/* Submit button */
.submit-row {
display: flex;
justify-content: center;
margin-top: 6px;
}
.submit {
appearance: none;
border: 2px solid var(--ink);
background: var(--ink);
color: var(--surface);
font-family: 'Alfa Slab One', serif;
font-weight: 400;
font-size: 18px;
letter-spacing: 0.18em;
text-transform: uppercase;
padding: 14px 30px;
border-radius: 4px;
cursor: pointer;
box-shadow: 6px 6px 0 var(--accent2);
transition: transform 90ms ease, box-shadow 90ms ease, background 120ms;
}
.submit:hover { background: #2a3251; }
.submit:active {
transform: translate(3px, 3px);
box-shadow: 3px 3px 0 var(--accent2);
}
.submit:disabled {
cursor: not-allowed;
opacity: 0.65;
box-shadow: 6px 6px 0 var(--ink-faint);
}
.submit .glyph { color: var(--accent2); margin: 0 10px; }
.status {
margin-top: 4px;
text-align: center;
min-height: 24px;
font-family: 'IBM Plex Mono', monospace;
font-size: 13px;
letter-spacing: 0.06em;
color: var(--ink-soft);
}
.status.error { color: var(--red); }
/* Output area */
.output-wrap {
margin-top: 40px;
}
@media (max-width: 600px) {
.page { padding: 24px 12px 60px; }
.panel { padding: 36px 22px 30px; }
.masthead h1 { font-size: 28px; }
}
</style>
</head>
<body>
<main class="page">
<section class="panel" aria-labelledby="title">
<span class="corner tl"></span>
<span class="corner tr"></span>
<span class="corner bl"></span>
<span class="corner br"></span>
<div class="masthead">
<div class="tiny">★ Presented by Hugging Face ★ Anno MMXXVI ★</div>
<h1 id="title">Trace <span class="accent">Personality</span> Bulletin</h1>
<div class="rule"><span class="line"></span>✺ ✺ ✺<span class="line"></span></div>
<p class="lede">
Drop a Hugging Face agent-traces dataset. The Roastery reads your
sessions and issues a printed bulletin of what the model thinks of you. Make sure to export your dataset with <a href="https://github.com/badlogic/pi-share-hf">this tool</a>
to remove secrets/PII before uploading the traces.
</p>
</div>
<form class="form" id="bulletin-form" autocomplete="off">
<div>
<div class="field-label">
<span>Dataset repo</span>
<span style="font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:0.2em;opacity:.6;">owner/name</span>
</div>
<input
id="repo"
class="repo-input"
type="text"
value="julien-c/pi-sessions"
spellcheck="false"
autocapitalize="off"
placeholder="owner/name"
/>
</div>
<div>
<div class="field-label">
<span>Max sessions</span>
<span class="value" id="n-value">30</span>
</div>
<div class="slider-wrap">
<input id="n" class="slider" type="range" min="1" max="50" step="1" value="30" />
<div class="slider-ticks">
<span>1</span><span>10</span><span>20</span><span>30</span><span>40</span><span>50</span>
</div>
</div>
</div>
<div class="submit-row">
<button class="submit" id="submit" type="submit">
<span class="glyph"></span>Issue my bulletin<span class="glyph"></span>
</button>
</div>
<div class="status" id="status" role="status" aria-live="polite">Awaiting bulletin…</div>
</form>
</section>
<div class="output-wrap" id="output"></div>
</main>
<script type="module">
import { Client } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js";
const $ = (id) => document.getElementById(id);
const form = $("bulletin-form");
const repoInput = $("repo");
const nInput = $("n");
const nValue = $("n-value");
const submitBtn = $("submit");
const statusEl = $("status");
const outputEl = $("output");
function paintSlider() {
const min = +nInput.min, max = +nInput.max;
const pct = ((+nInput.value - min) * 100) / (max - min);
nInput.style.setProperty("--pct", pct + "%");
nValue.textContent = nInput.value;
}
nInput.addEventListener("input", paintSlider);
paintSlider();
function setStatus(text, isError = false) {
statusEl.textContent = text || "";
statusEl.classList.toggle("error", !!isError);
}
let clientPromise = null;
function getClient() {
if (!clientPromise) clientPromise = Client.connect(window.location.origin);
return clientPromise;
}
form.addEventListener("submit", async (e) => {
e.preventDefault();
const repo_id = repoInput.value.trim();
const max_sessions = parseInt(nInput.value, 10);
if (!repo_id) {
setStatus("Enter a dataset repo first.", true);
return;
}
submitBtn.disabled = true;
setStatus("Connecting…");
outputEl.innerHTML = "";
try {
const client = await getClient();
const job = client.submit("/generate_bulletin", {
repo_id,
max_sessions,
});
let lastHtml = "";
for await (const msg of job) {
if (msg.type !== "data") continue;
const [status, html] = msg.data || [];
if (typeof status === "string") {
setStatus(status, status.startsWith("❌"));
}
if (typeof html === "string") {
lastHtml = html;
}
}
if (lastHtml) {
outputEl.innerHTML = lastHtml;
// Execute inline scripts injected by the response (bulletin uses iframe srcdoc, so usually a no-op).
outputEl.querySelectorAll("script").forEach((old) => {
const s = document.createElement("script");
for (const a of old.attributes) s.setAttribute(a.name, a.value);
s.text = old.text;
old.replaceWith(s);
});
}
} catch (err) {
console.error(err);
setStatus("❌ " + (err && err.message ? err.message : String(err)), true);
} finally {
submitBtn.disabled = false;
}
});
</script>
</body>
</html>