Keep the table inside the viewport, and make the structure copyable
Browse filesPrinting whole SMILES pushed the table past the screen - fifty characters in a column
sharing space with six score columns. Truncating alone would have made the structure
useless, since the point of showing it is that someone can take it, so the cell now shows
a prefix with a copy button that puts the full string on the clipboard. The button stops
the click reaching the row, which would otherwise toggle the overlay underneath.
The page still scrolled sideways after that, for an unrelated reason: a grid item defaults
to min-width:auto, so the leaderboard column would not shrink below the table's min-width
and shoved the submission card off-screen.
Withheld structures get no copy button - there is nothing to copy and offering one would
imply otherwise.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- index.html +32 -7
|
@@ -182,6 +182,9 @@
|
|
| 182 |
.legend{display:flex;gap:14px;flex-wrap:wrap;margin-top:12px;font-size:11px;color:var(--muted)}
|
| 183 |
.legend i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:5px;vertical-align:-1px}
|
| 184 |
.grid{display:grid;grid-template-columns:1fr 348px;gap:20px;align-items:start;margin-top:20px}
|
|
|
|
|
|
|
|
|
|
| 185 |
@media(max-width:960px){.grid{grid-template-columns:1fr}#langbar{position:static;margin-bottom:14px;width:max-content}#langnote{position:static;margin-bottom:8px}}
|
| 186 |
.card{background:var(--panel);border:1px solid var(--line);border-radius:16px;
|
| 187 |
padding:18px 20px;box-shadow:var(--shadow)}
|
|
@@ -208,9 +211,15 @@
|
|
| 208 |
.rk.top{color:var(--accent);font-size:15px}
|
| 209 |
.cid{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;font-weight:600}
|
| 210 |
.skel{color:#93a1b5;font-size:11px;font-family:ui-monospace,monospace}
|
| 211 |
-
.
|
| 212 |
-
|
|
|
|
| 213 |
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
.lock{color:#93a1b5;font-size:10.5px;margin-top:1px;display:block}
|
| 215 |
#ov .osmi{margin-top:10px;padding:9px 10px;background:#f6f8fc;border:1px solid var(--line);
|
| 216 |
border-radius:8px;font-family:ui-monospace,Menlo,monospace;font-size:10.5px;
|
|
@@ -429,7 +438,7 @@ const T = {
|
|
| 429 |
synthesis:d=>`ํฉ์ฑ๋์ด๋ SA ${d.sa_score ?? "โ"} (1 ์ฌ์ ~ 10 ์ด๋ ค์)`,
|
| 430 |
},
|
| 431 |
ov_pred:"์์ธก์น", ov_pf:"๋ง๋ผ๋ฆฌ์ ํจ์", ov_hs:"์ฌ๋ ํจ์", ov_sel:"์ ํ๋",
|
| 432 |
-
private_note:"์ ์ถ์๊ฐ ๋น๊ณต๊ฐ๋ฅผ ์ ํ
|
| 433 |
private_full:"์ ์ถ์๊ฐ ๋น๊ณต๊ฐ๋ฅผ ์ ํํ์ต๋๋ค. ํ๋ณด ID์ ๊ณจ๊ฒฉ ํด์๋ก๋ง ์๋ณ๋ฉ๋๋ค.",
|
| 434 |
reasons:{
|
| 435 |
parse: r=>"๊ตฌ์กฐ๋ฅผ ํด์ํ ์ ์์ต๋๋ค (SMILES ๋๋ InChI๋ก ์ ์ถํ์ธ์)",
|
|
@@ -523,7 +532,7 @@ const T = {
|
|
| 523 |
synthesis:d=>`SA score ${d.sa_score ?? "โ"} (1 easy - 10 hard)`,
|
| 524 |
},
|
| 525 |
ov_pred:"Predicted", ov_pf:"parasite enzyme", ov_hs:"human enzyme", ov_sel:"selectivity",
|
| 526 |
-
private_note:"structure withheld by the entrant",
|
| 527 |
private_full:"The entrant chose to keep this structure private. It is identified only by its candidate ID and skeleton hash.",
|
| 528 |
reasons:{
|
| 529 |
parse: r=>"Structure could not be parsed (submit SMILES or InChI)",
|
|
@@ -687,11 +696,16 @@ function render(){
|
|
| 687 |
+ '</span>';
|
| 688 |
const _m = e.is_anchor ? (e.note || d.ref) : (e.model_name || "\u2013");
|
| 689 |
const who = `<span class="who" title="${_m}">${_m}</span>`;
|
| 690 |
-
// the structure is the name now; a withheld one shows its ends, middle blanked
|
| 691 |
-
|
|
|
|
|
|
|
|
|
|
| 692 |
const id = e.is_anchor ? `<b>${e.candidate_id}</b>`
|
| 693 |
: (shown
|
| 694 |
-
? `<span class="smi2" title="${shown}">${
|
|
|
|
|
|
|
| 695 |
: `<span class="cid">${e.candidate_id}</span>`);
|
| 696 |
const tag = e.is_anchor ? `<span class="tag a">${d.anchor}</span>`
|
| 697 |
: (e.tier===2 ? `<span class="tag r">${d.relegated}</span>` : "");
|
|
@@ -704,6 +718,17 @@ function render(){
|
|
| 704 |
<span class="tot">${f1(e.total)}</span></div></td></tr>`;
|
| 705 |
}).join("") : `<tr><td colspan="11" class="who">${d.empty}</td></tr>`;
|
| 706 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 707 |
// rows carry their entry so the overlay never has to look anything up again
|
| 708 |
[...document.querySelectorAll("#rows tr")].forEach((tr, i) => {
|
| 709 |
const e = rows[i];
|
|
|
|
| 182 |
.legend{display:flex;gap:14px;flex-wrap:wrap;margin-top:12px;font-size:11px;color:var(--muted)}
|
| 183 |
.legend i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:5px;vertical-align:-1px}
|
| 184 |
.grid{display:grid;grid-template-columns:1fr 348px;gap:20px;align-items:start;margin-top:20px}
|
| 185 |
+
/* A grid item defaults to min-width:auto, so the 1fr column refused to shrink below
|
| 186 |
+
the table's min-width and pushed the submission card off-screen. */
|
| 187 |
+
.grid > *{min-width:0}
|
| 188 |
@media(max-width:960px){.grid{grid-template-columns:1fr}#langbar{position:static;margin-bottom:14px;width:max-content}#langnote{position:static;margin-bottom:8px}}
|
| 189 |
.card{background:var(--panel);border:1px solid var(--line);border-radius:16px;
|
| 190 |
padding:18px 20px;box-shadow:var(--shadow)}
|
|
|
|
| 211 |
.rk.top{color:var(--accent);font-size:15px}
|
| 212 |
.cid{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;font-weight:600}
|
| 213 |
.skel{color:#93a1b5;font-size:11px;font-family:ui-monospace,monospace}
|
| 214 |
+
.smiwrap{display:flex;align-items:center;gap:6px;max-width:250px}
|
| 215 |
+
.smi2{color:#1c2a3d;font-size:12px;font-weight:600;
|
| 216 |
+
font-family:ui-monospace,Menlo,Consolas,monospace;
|
| 217 |
overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
| 218 |
+
.cp{flex:none;border:1px solid var(--line);background:#fff;border-radius:6px;
|
| 219 |
+
width:22px;height:22px;line-height:1;cursor:pointer;color:var(--muted);
|
| 220 |
+
font-size:11px;padding:0}
|
| 221 |
+
.cp:hover{border-color:var(--accent);color:var(--accent);background:var(--accent-soft)}
|
| 222 |
+
.cp.done{border-color:var(--accent);color:#fff;background:var(--accent)}
|
| 223 |
.lock{color:#93a1b5;font-size:10.5px;margin-top:1px;display:block}
|
| 224 |
#ov .osmi{margin-top:10px;padding:9px 10px;background:#f6f8fc;border:1px solid var(--line);
|
| 225 |
border-radius:8px;font-family:ui-monospace,Menlo,monospace;font-size:10.5px;
|
|
|
|
| 438 |
synthesis:d=>`ํฉ์ฑ๋์ด๋ SA ${d.sa_score ?? "โ"} (1 ์ฌ์ ~ 10 ์ด๋ ค์)`,
|
| 439 |
},
|
| 440 |
ov_pred:"์์ธก์น", ov_pf:"๋ง๋ผ๋ฆฌ์ ํจ์", ov_hs:"์ฌ๋ ํจ์", ov_sel:"์ ํ๋",
|
| 441 |
+
private_note:"์ ์ถ์๊ฐ ๋น๊ณต๊ฐ๋ฅผ ์ ํํ์ต๋๋ค", copy_smiles:"๋ถ์์ ๋ณต์ฌ",
|
| 442 |
private_full:"์ ์ถ์๊ฐ ๋น๊ณต๊ฐ๋ฅผ ์ ํํ์ต๋๋ค. ํ๋ณด ID์ ๊ณจ๊ฒฉ ํด์๋ก๋ง ์๋ณ๋ฉ๋๋ค.",
|
| 443 |
reasons:{
|
| 444 |
parse: r=>"๊ตฌ์กฐ๋ฅผ ํด์ํ ์ ์์ต๋๋ค (SMILES ๋๋ InChI๋ก ์ ์ถํ์ธ์)",
|
|
|
|
| 532 |
synthesis:d=>`SA score ${d.sa_score ?? "โ"} (1 easy - 10 hard)`,
|
| 533 |
},
|
| 534 |
ov_pred:"Predicted", ov_pf:"parasite enzyme", ov_hs:"human enzyme", ov_sel:"selectivity",
|
| 535 |
+
private_note:"structure withheld by the entrant", copy_smiles:"Copy SMILES",
|
| 536 |
private_full:"The entrant chose to keep this structure private. It is identified only by its candidate ID and skeleton hash.",
|
| 537 |
reasons:{
|
| 538 |
parse: r=>"Structure could not be parsed (submit SMILES or InChI)",
|
|
|
|
| 696 |
+ '</span>';
|
| 697 |
const _m = e.is_anchor ? (e.note || d.ref) : (e.model_name || "\u2013");
|
| 698 |
const who = `<span class="who" title="${_m}">${_m}</span>`;
|
| 699 |
+
// the structure is the name now; a withheld one shows its ends, middle blanked.
|
| 700 |
+
// Only a published structure gets a copy button - there is nothing to copy otherwise.
|
| 701 |
+
const isPub = e.visibility === "public" && !!e.smiles;
|
| 702 |
+
const shown = isPub ? e.smiles : e.smiles_masked;
|
| 703 |
+
const clip = shown ? (shown.length > 26 ? shown.slice(0, 26) + "\u2026" : shown) : "";
|
| 704 |
const id = e.is_anchor ? `<b>${e.candidate_id}</b>`
|
| 705 |
: (shown
|
| 706 |
+
? `<span class="smiwrap"><span class="smi2" title="${shown}">${clip}</span>`
|
| 707 |
+
+ (isPub ? `<button class="cp" data-smi="${e.smiles}" title="${d.copy_smiles}">\u29c9</button>` : "")
|
| 708 |
+
+ `</span>`
|
| 709 |
: `<span class="cid">${e.candidate_id}</span>`);
|
| 710 |
const tag = e.is_anchor ? `<span class="tag a">${d.anchor}</span>`
|
| 711 |
: (e.tier===2 ? `<span class="tag r">${d.relegated}</span>` : "");
|
|
|
|
| 718 |
<span class="tot">${f1(e.total)}</span></div></td></tr>`;
|
| 719 |
}).join("") : `<tr><td colspan="11" class="who">${d.empty}</td></tr>`;
|
| 720 |
|
| 721 |
+
$("#rows").querySelectorAll(".cp").forEach(b => b.onclick = async (ev) => {
|
| 722 |
+
// without this the click also reaches the row and toggles the overlay
|
| 723 |
+
ev.stopPropagation();
|
| 724 |
+
const v = b.dataset.smi;
|
| 725 |
+
try{ await navigator.clipboard.writeText(v); }
|
| 726 |
+
catch(_){ const ta=document.createElement("textarea"); ta.value=v;
|
| 727 |
+
document.body.appendChild(ta); ta.select(); document.execCommand("copy"); ta.remove(); }
|
| 728 |
+
b.classList.add("done"); b.textContent = "\u2713";
|
| 729 |
+
setTimeout(()=>{ b.classList.remove("done"); b.textContent = "\u29c9"; }, 1200);
|
| 730 |
+
});
|
| 731 |
+
|
| 732 |
// rows carry their entry so the overlay never has to look anything up again
|
| 733 |
[...document.querySelectorAll("#rows tr")].forEach((tr, i) => {
|
| 734 |
const e = rows[i];
|