procgrep-explorer / static /index.html
midah's picture
Consolidated explorer shell on the Space: sidebar nav (eval/sessions toggle), bundled demo session, footer cross-nav to essay/arXiv/github; relative same-origin fetch; +2 datasets in SUGGESTED
c868574 verified
Raw
History Blame Contribute Delete
11.7 kB
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>procgrep · query agent traces, live</title>
<link rel="stylesheet" href="/static/theme.css">
<link rel="stylesheet" href="/static/style.css">
<style>
/* Global sidebar nav, mirrored from the published explorer so the Space and the
Pages site share one chrome: a surface toggle (eval/sessions), the per-surface
nav, a session list the embedded panel fills, and a footer out to the essay,
arXiv, and GitHub. The eval content keeps the live query + compare views and
talks to this same backend over same-origin relative paths. */
body{padding-left:212px}
.railnav{position:fixed;left:0;top:0;bottom:0;width:212px;padding:22px 14px;overflow:auto;background:#EFEAE2;display:flex;flex-direction:column}
.railnav .mark{margin:0 6px 14px;font-weight:600;display:inline-flex;align-items:center;gap:9px;color:var(--ink);text-decoration:none}
.railnav .mark::before{content:"";width:8px;height:8px;background:var(--copper);border-radius:1px}
.railnav .surf{display:flex;gap:3px;margin:0 0 16px;background:#E2DBD0;border-radius:7px;padding:3px}
.railnav .surf button{flex:1;text-align:center;font:11px var(--mono);border:0;background:transparent;color:var(--olive);padding:6px 3px;border-radius:5px;cursor:pointer}
.railnav .surf button.on{background:var(--ink);color:#fff}
.railnav .railfoot{margin-top:auto;border-top:1px solid var(--rule);padding-top:10px;display:flex;flex-direction:column;gap:1px}
.railnav .railfoot a{display:flex;align-items:center;gap:8px;font-size:12px;padding:5px 6px;border-radius:6px;text-decoration:none;color:var(--olive)}
.railnav .railfoot a:hover{color:var(--ink);background:rgba(20,17,14,.06)}
.railnav .railfoot .ico{flex:0 0 15px;width:15px;height:15px;display:inline-flex;align-items:center;justify-content:center}
.railnav .railfoot .ico svg{width:14px;height:14px;fill:currentColor}
.railnav .railfoot .ext{margin-left:auto;font-size:10px;color:var(--olive)}
.railnav .railfoot .meta{margin-top:9px;padding:0 6px;font-size:10.5px;line-height:1.55;color:var(--olive)}
.railnav #sesslist .sessitem{display:block;line-height:1.32;padding:7px 10px;text-align:left}
.railnav #sesslist .sessitem .nnm{display:block;font-size:13px}
.railnav #sesslist .sessitem .smeta{display:block;font-size:10px;color:var(--olive)}
.railnav #sesslist .sessitem.act .smeta{color:#cfc9bf}
.railnav h2{font-size:10px;letter-spacing:.07em;text-transform:uppercase;color:var(--olive);margin:0 0 12px;font-weight:600}
.railnav .ritem{display:block;width:100%;text-align:left;font-family:var(--mono);font-size:13px;border:0;background:transparent;color:var(--ink);padding:8px 10px;border-radius:6px;cursor:pointer;margin:0 0 2px}
.railnav .ritem:hover{background:#ECE7DF}
.railnav .ritem.act{background:var(--ink);color:#fff}
.cgroup{display:contents}
body.corpus-eval .cgroup[data-c="sess"]{display:none}
body.corpus-sess .cgroup[data-c="eval"]{display:none}
/* The wrap drops the explorer's own top tab row; the rail is the nav now. */
.tabs{display:none}
#sessframe{width:100%;height:80vh;border:0;border-radius:6px;background:#fff}
.sesscallout{margin:14px 0 0;padding:10px 14px;border:1px solid var(--rule);background:#fff;color:var(--olive);font-size:12px;line-height:1.55}
.sesscallout code{background:#efeae2;padding:1px 6px;border-radius:3px;color:var(--ink)}
@media(max-width:720px){
body{padding-left:0}
.railnav{position:static;width:auto;height:auto;padding:12px 16px;background:#EFEAE2;
display:flex;flex-wrap:wrap;align-items:center;gap:4px}
.railnav h2{margin:0 4px 0 0;font-size:9px}
.railnav .ritem{display:inline-block;width:auto;padding:6px 9px;margin:0}
.railnav .railfoot{flex-direction:row;flex-wrap:wrap;margin:0 0 0 8px;border:0;padding:0}
.railnav .railfoot .meta{display:none}
}
</style>
</head><body class="corpus-eval">
<nav class="railnav">
<a class="mark" href="/">procgrep</a>
<div class="surf">
<button id="c-eval" class="on" onclick="pickCorpus('eval')">eval</button>
<button id="c-sess" onclick="pickCorpus('sess')">sessions</button>
</div>
<div class="cgroup" data-c="eval">
<button class="ritem act" id="nav-query" onclick="showView('query')">query</button>
<button class="ritem" id="nav-compare" onclick="showView('compare')">compare</button>
</div>
<div class="cgroup" data-c="sess">
<div id="sesslist"><span class="dim" style="display:block;padding:8px 10px;font-size:11px">loading sessions…</span></div>
</div>
<div class="railfoot">
<a href="https://hamidah.me/procgrep/"><span class="ico"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M3 1.5h10A1.5 1.5 0 0 1 14.5 3v10a1.5 1.5 0 0 1-1.5 1.5H3A1.5 1.5 0 0 1 1.5 13V3A1.5 1.5 0 0 1 3 1.5zM4.5 5h7v1.2h-7V5zm0 2.6h7v1.2h-7V7.6zm0 2.6h4.5v1.2H4.5v-1.2z"/></svg></span>essay<span class="ext"></span></a>
<a href="https://arxiv.org/abs/2606.16988"><span class="ico"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M9 1H4a1.6 1.6 0 0 0-1.6 1.6v10.8A1.6 1.6 0 0 0 4 15h8a1.6 1.6 0 0 0 1.6-1.6V5.5L9 1zm0 1.7L12.3 6H9V2.7zM5 8.4h6V9.6H5V8.4zm0 2.4h6V12H5v-1.2z"/></svg></span>arXiv<span class="ext"></span></a>
<a href="https://github.com/hamidahoderinwale/procgrep"><span class="ico"><svg viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.02-1.49-2.22.48-2.69-1.07-2.69-1.07-.36-.92-.89-1.17-.89-1.17-.73-.5.05-.49.05-.49.81.06 1.23.83 1.23.83.72 1.23 1.88.88 2.34.67.07-.52.28-.88.51-1.08-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.83-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.6 7.6 0 0 1 2-.27c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.52.56.83 1.28.83 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8 8 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg></span>github<span class="ext"></span></a>
<div class="meta">June 2026</div>
</div>
</nav>
<div class="wrap">
<!-- app.js's showView toggles these tab elements; the rail is the visible nav so
they are kept hidden purely to satisfy that DOM contract. -->
<nav class="tabs" hidden><span class="tab" id="tab-query"></span><span class="tab" id="tab-compare"></span></nav>
<section id="view-query">
<h1>Grep agent traces, live.</h1>
<p class="sub">Ask a structural question about how an agent worked and get an exact answer over the whole
trajectory dataset, not a sample. ProcGrep runs server-side: it ingests the dataset, canonicalizes it
into an action vocabulary, and scans every trace. No model call.</p>
<div class="dsbar">dataset:
<select id="ds"></select>
<input id="dsfree" class="dsfree" placeholder="or paste any HF dataset id" autocomplete="off">
<span id="dsmeta" class="dim"></span>
<a id="dsource" class="dim src" target="_blank" rel="noopener"></a>
</div>
<p class="note prov">These are public agent-rollout corpora on Hugging Face (SWE-agent and OpenHands
scaffolds solving SWE-bench-family GitHub issues). ProcGrep canonicalizes each trajectory into an action spine; nothing is re-hosted.</p>
<div class="qbar"><span class="mag"></span>
<input id="q" placeholder="atom pattern, e.g. (edit ){5,} or pick one below" autocomplete="off"></div>
<div class="tryline">try: <span id="trychips"></span> <span class="chip" id="noisetog">hide think/other</span></div>
<div id="res"></div>
</section>
<section id="view-compare" class="hidden">
<h1>Two groups, side by side.</h1>
<p class="sub">The diff strip names what separates two groups: the procedures that most distinguish them, their
action-mix distance, and the length and reasoning gaps. No model call.</p>
<div class="cmpbar">
<span class="seg" id="seg-outcome" onclick="setAxis('outcome')">by outcome</span>
<span class="seg" id="seg-agent" onclick="setAxis('agent')">by agent</span>
<span class="seg" id="seg-eval" onclick="setAxis('eval')">by eval</span>
<span id="cmp-controls"></span>
</div>
<div id="cmp-res"></div>
</section>
<section id="view-sessions" class="hidden">
<h1>One session, as a procedural fingerprint.</h1>
<p class="sub">A real Claude Code session reduced to its action structure: each human prompt opens a turn,
and the agent's tool calls become the turn's atoms. Pick it in the sidebar, replay it, or run an atom
pattern over it — the same grammar as the eval query.</p>
<iframe id="sessframe" title="live procedural fingerprint"></iframe>
<p class="sesscallout">These are sample traces. Run procgrep locally to explore your own sessions:
<code>python examples/procgrep_view.py</code></p>
</section>
</div>
<script src="/static/app.js"></script>
<script>
// Surface toggle + session-list plumbing for the global rail. The eval views
// (query/compare) live in app.js and already fetch this backend over relative
// paths; this script only adds the eval/sessions switch and wires the sessions
// rail to the embedded fingerprint panel. One rail, not two.
const _origShowView = window.showView;
window.showView = function(name, axis){
document.getElementById("view-query").classList.toggle("hidden", name !== "query");
document.getElementById("view-compare").classList.toggle("hidden", name !== "compare");
document.getElementById("view-sessions").classList.toggle("hidden", name !== "sessions");
document.getElementById("nav-query").classList.toggle("act", name === "query");
document.getElementById("nav-compare").classList.toggle("act", name === "compare");
if(name === "sessions"){ ensureSessFrame(); return; }
// Defer to app.js for the eval views (it owns the comparator init/axis logic).
if(_origShowView) _origShowView(name, axis);
};
let _sessLoaded = false;
function ensureSessFrame(){
const f = document.getElementById("sessframe");
if(!_sessLoaded){ _sessLoaded = true; f.src = "/static/live_fingerprint.html?embed=1&demo=1"; }
}
function pickCorpus(c){
document.body.classList.remove("corpus-eval","corpus-sess");
document.body.classList.add("corpus-" + c);
document.getElementById("c-eval").classList.toggle("on", c === "eval");
document.getElementById("c-sess").classList.toggle("on", c === "sess");
showView(c === "sess" ? "sessions" : "query");
}
// The embedded panel posts its session list up; render it in the global rail and
// post the selection back so it loads in the iframe. Mirrors the Pages explorer.
function renderSessList(msg){
const el = document.getElementById("sesslist"); if(!el) return;
const active = msg.hasOverview ? "ov" : (msg.list[0] ? msg.list[0].i : null);
let html = msg.hasOverview
? `<button class="ritem sessitem${active==="ov"?" act":""}" onclick="selectSession(null,this)"><span class="nnm">overview</span><span class="smeta">run length across sessions</span></button>`
: "";
html += msg.list.map(s =>
`<button class="ritem sessitem${active===s.i?" act":""}" onclick="selectSession(${s.i},this)"><span class="nnm">${s.name}</span><span class="smeta">${s.client}${s.date?` · ${s.date}`:""}${s.dur?` · ${s.dur}`:""}</span></button>`
).join("");
el.innerHTML = html || '<span class="dim" style="display:block;padding:8px 10px;font-size:11px">no sessions</span>';
}
function selectSession(i, btn){
const f = document.getElementById("sessframe");
if(f && f.contentWindow) f.contentWindow.postMessage({type:"procgrep-select", index:i}, "*");
document.querySelectorAll("#sesslist .sessitem").forEach(b => b.classList.remove("act"));
if(btn) btn.classList.add("act");
}
window.addEventListener("message", e => {
if(e.data && e.data.type === "procgrep-sessions") renderSessList(e.data);
});
</script>
</body></html>