oncodsl / web /app /PasteToDraw.tsx
govindbalki's picture
Upload folder using huggingface_hub
0fff343 verified
Raw
History Blame Contribute Delete
8.5 kB
"use client";
import { useId, useState } from "react";
import { safeParseProgramRepr } from "../lib/programRepr";
interface Props {
text: string;
onTextChange: (next: string) => void;
onDraw: (repr: string, label: string) => void;
}
const PLACEHOLDER =
"Combine(Reduce(Select(M,[g05347,g00048]),mean),Reduce(Select(M,[g06271]),max),sub)";
export default function PasteToDraw({ text, onTextChange, onDraw }: Props) {
const [error, setError] = useState<string | null>(null);
const [showFormat, setShowFormat] = useState(false);
const panelId = useId();
function draw() {
const trimmed = text.trim();
if (!trimmed) {
setError("Paste a program first.");
return;
}
const { ok, error: pErr } = safeParseProgramRepr(trimmed);
if (!ok) {
setError(`Couldn't parse: ${pErr}`);
return;
}
setError(null);
onDraw(trimmed, "pasted program");
}
return (
<div>
<label
className="mb-1 block text-[11px] font-medium tracking-wide text-muted"
htmlFor={`${panelId}-textarea`}
>
Paste a program
</label>
<textarea
id={`${panelId}-textarea`}
value={text}
onChange={(e) => onTextChange(e.target.value)}
placeholder={PLACEHOLDER}
spellCheck={false}
rows={2}
className="w-full rounded-md border border-border bg-white px-3 py-2 font-mono text-xs text-ink focus:border-accent focus:outline-none"
/>
<p className="mt-1 text-[11px] text-muted">
Paste a program here, or click any tile to load it. Use Copy to grab
a program&apos;s text. Reveal / evaluate is only available for actual
run winners β€” pasted programs render as opaque IDs only.
</p>
<div className="mt-2 flex items-center justify-end gap-3">
<button
type="button"
onClick={draw}
className="rounded-md bg-accent px-3 py-1.5 text-xs font-medium text-card hover:bg-ink"
>
Draw
</button>
</div>
{error && (
<p className="mt-2 rounded-md border border-highlight/40 bg-highlight/10 px-3 py-2 text-xs text-highlight">
{error}
</p>
)}
<div className="mt-3 text-xs">
<button
type="button"
onClick={() => setShowFormat((v) => !v)}
aria-expanded={showFormat}
aria-controls={`${panelId}-format`}
className="inline-flex items-center gap-1.5 rounded text-ink hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
>
<span
aria-hidden
style={{
display: "inline-block",
transition: "transform 120ms ease",
transform: showFormat ? "rotate(90deg)" : "rotate(0deg)",
}}
>
β–Έ
</span>
<span className="font-medium">Format &amp; examples</span>
</button>
{showFormat && <FormatGuide id={`${panelId}-format`} />}
</div>
</div>
);
}
function FormatGuide({ id }: { id: string }) {
return (
<div
id={id}
className="mt-3 rounded-md border border-border bg-bg p-4 text-[12.5px] leading-relaxed text-ink"
>
<p className="mb-3">
<strong>What a program is:</strong> a recipe that turns gene
expression into ONE score per patient.
</p>
<p className="mb-2 font-semibold text-ink">The pieces:</p>
<ul className="mb-3 ml-5 list-disc space-y-1 marker:text-muted">
<li>
<code className="font-mono text-ink">M</code> β€” the expression
matrix (all patients Γ— genes); the starting data.
</li>
<li>
<code className="font-mono text-ink">Select(M, [g#####, ...])</code>{" "}
β€” pick a group of genes β†’ a smaller matrix.
</li>
<li>
<code className="font-mono text-ink">Search(M, k)</code> β€” bounded
nested search: keep the top-k columns by univariate target
association (gated; experimental).
</li>
<li>
<code className="font-mono text-ink">Reduce(&lt;matrix&gt;, &lt;agg&gt;)</code>{" "}
β€” collapse a matrix into one score per patient. {" "}
<code className="font-mono text-ink">&lt;agg&gt;</code> ={" "}
<code className="font-mono text-ink">mean</code> |{" "}
<code className="font-mono text-ink">median</code> |{" "}
<code className="font-mono text-ink">max</code> |{" "}
<code className="font-mono text-ink">min</code> |{" "}
<code className="font-mono text-ink">var</code>.
</li>
<li>
<code className="font-mono text-ink">Combine(&lt;scoreA&gt;, &lt;scoreB&gt;, &lt;op&gt;)</code>{" "}
β€” merge two scores into one.{" "}
<code className="font-mono text-ink">&lt;op&gt;</code> ={" "}
<code className="font-mono text-ink">add</code> |{" "}
<code className="font-mono text-ink">sub</code> |{" "}
<code className="font-mono text-ink">mul</code> |{" "}
<code className="font-mono text-ink">protected_div</code> (safe) |{" "}
<code className="font-mono text-ink">mean</code>.
</li>
<li>
<code className="font-mono text-ink">Split(&lt;score&gt;, &lt;predicate&gt;)</code>{" "}
β€” partition patients into two branches by a name-blind predicate
(<code className="font-mono text-ink">score</code> = above /
below median; <code className="font-mono text-ink">stage_late</code>{" "}
= AJCC stage III/IV), mean-centre per side, then recombine. One
level of Split only.
</li>
<li>
<code className="font-mono text-ink">FitApply(&lt;score&gt;, &lt;target&gt;)</code>{" "}
β€” train a small model on the score against the chosen label
(<code className="font-mono text-ink">msi</code> or{" "}
<code className="font-mono text-ink">tmb</code>) and emit
predicted probabilities / values back to the program.
</li>
<li>
<code className="font-mono text-ink">Associate(&lt;score&gt;, &lt;target&gt;, &lt;kind&gt;)</code>{" "}
β€” Scalar output: plain observational correlation against the
target. <code className="font-mono text-ink">&lt;kind&gt;</code>{" "}
= <code className="font-mono text-ink">spearman</code> |{" "}
<code className="font-mono text-ink">pearson</code>.
</li>
<li>
<code className="font-mono text-ink">Effect(&lt;score&gt;, &lt;target&gt;, &lt;kind&gt;)</code>{" "}
β€” Scalar output: same correlation AFTER residualising on the
named clinical confounders (stage + age). Observational
backdoor adjustment β€” only as good as the measured confounders.
</li>
<li>
<strong>Output:</strong> one score per patient (or a Scalar for
Associate / Effect-rooted programs), used to satisfy the
objective.
</li>
</ul>
<p className="mb-2 font-semibold text-ink">Rules:</p>
<ul className="mb-3 ml-5 list-disc space-y-1 marker:text-muted">
<li>
Gene IDs are the opaque{" "}
<code className="font-mono text-ink">g#####</code> IDs shown on
the tiles and in the result.
</li>
<li>
List a <code className="font-mono text-ink">Select</code>&apos;s
genes in <code className="font-mono text-ink">[square brackets]</code>
, comma-separated.
</li>
<li>Spaces are optional β€” paste with or without them.</li>
<li>
Every <code className="font-mono text-ink">Reduce</code> should
wrap a <code className="font-mono text-ink">Select(...)</code> so it
reduces a chosen group, not the whole matrix.
</li>
</ul>
<p className="mb-1 font-semibold text-ink">Annotated example:</p>
<p className="mb-1 font-mono text-[12px] text-ink">
Combine(
<br />
&nbsp;&nbsp;Reduce(Select(M,[g05347,g00048]), mean),
<br />
&nbsp;&nbsp;Reduce(Select(M,[g06271]), max),
<br />
&nbsp;&nbsp;sub
<br />)
</p>
<p className="text-muted">
β†’ average g05347 &amp; g00048 β†’ scoreA; take the max of g06271 β†’
scoreB; final score = scoreA βˆ’ scoreB.
</p>
</div>
);
}