VIDRAFT Claude Opus 5 commited on
Commit
39ebfa8
路
1 Parent(s): ca1ba69

Keep the detail overlay off the copy button

Browse files

The overlay opened aligned to the row's left edge - exactly where the structure and its
copy button sit - so hovering a row buried the control the reader was reaching for. It now
anchors to the right of the row, leaving the structure column clear, and carries its own
copy button so the full string is available from the panel that is already open.

The panel accepts clicks now rather than being click-through, which it has to be to hold a
button, and a short grace period on leaving a row means moving the pointer into the panel
does not dismiss it first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Files changed (1) hide show
  1. index.html +29 -16
index.html CHANGED
@@ -75,7 +75,7 @@
75
 
76
  #ov{position:fixed;z-index:50;display:none;width:340px;background:#fff;
77
  border:1px solid var(--line);border-radius:14px;padding:15px 16px;
78
- box-shadow:0 12px 40px rgba(19,26,38,.18);pointer-events:none}
79
  #ov h4{margin:0 0 2px;font-size:14px;font-weight:800;letter-spacing:-.2px}
80
  #ov .oid{font-size:11px;color:var(--muted);font-family:ui-monospace,monospace;margin-bottom:10px}
81
  #ov .orow{display:grid;grid-template-columns:60px 1fr 42px;gap:8px;align-items:center;
@@ -223,7 +223,8 @@
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;
226
- color:#31465e;line-height:1.5;word-break:break-all}
 
227
  .who{color:var(--muted);font-size:12.5px}
228
  /* Long model identifiers wrapped onto four lines and made every row three times
229
  taller than it needed to be. Truncate; the full value is on the tooltip. */
@@ -718,23 +719,15 @@ function render(){
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];
735
  if(!e) return;
736
- tr.onmouseenter = () => showOverlay(e, tr.getBoundingClientRect());
737
- tr.onmouseleave = hideOverlay;
 
738
  // touch has no hover, so a tap toggles it
739
  tr.onclick = () => (OV.style.display === "block" && OV.dataset.id === e.candidate_id)
740
  ? hideOverlay()
@@ -935,13 +928,28 @@ function overlayHTML(e){
935
  ? `<div class="oflag">${e.relegate_reason.map(reasonText).join(" / ")}</div>` : "";
936
  const who = e.is_anchor ? (e.note||"") : [e.hf_user, e.model_name].filter(Boolean).join(" 路 ");
937
  const full = (e.visibility === "public" && e.smiles) ? e.smiles : e.smiles_masked;
 
938
  const smi = e.is_anchor ? ""
939
  : `<div class="osmi">${full || ""}`
 
940
  + (e.visibility === "public" ? "" : `<br>馃敀 ${d.private_full}`) + `</div>`;
941
  return `<h4>${e.candidate_id}</h4><div class="oid">${who}${e.skeleton?" 路 "+e.skeleton:""}</div>`
942
  + rows + pred + smi + flag + `<div class="ofoot">${d.ov_foot}</div>`;
943
  }
944
 
 
 
 
 
 
 
 
 
 
 
 
 
 
945
  function showOverlay(e, rect){
946
  OV.innerHTML = overlayHTML(e);
947
  OV.style.display = "block";
@@ -949,11 +957,16 @@ function showOverlay(e, rect){
949
  // flip above the row rather than open past the bottom of the window
950
  let top = rect.bottom + 8;
951
  if(top + h > window.innerHeight - 8) top = Math.max(8, rect.top - h - 8);
952
- let left = Math.min(rect.left, window.innerWidth - w - 12);
 
 
953
  OV.style.top = top + "px";
954
- OV.style.left = Math.max(8, left) + "px";
 
955
  }
956
  function hideOverlay(){ OV.style.display = "none"; }
 
 
957
 
958
  async function load(){
959
  try{
 
75
 
76
  #ov{position:fixed;z-index:50;display:none;width:340px;background:#fff;
77
  border:1px solid var(--line);border-radius:14px;padding:15px 16px;
78
+ box-shadow:0 12px 40px rgba(19,26,38,.18)}
79
  #ov h4{margin:0 0 2px;font-size:14px;font-weight:800;letter-spacing:-.2px}
80
  #ov .oid{font-size:11px;color:var(--muted);font-family:ui-monospace,monospace;margin-bottom:10px}
81
  #ov .orow{display:grid;grid-template-columns:60px 1fr 42px;gap:8px;align-items:center;
 
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;
226
+ color:#31465e;line-height:1.5;word-break:break-all;position:relative}
227
+ #ov .osmi .cp{position:absolute;top:6px;right:6px}
228
  .who{color:var(--muted);font-size:12.5px}
229
  /* Long model identifiers wrapped onto four lines and made every row three times
230
  taller than it needed to be. Truncate; the full value is on the tooltip. */
 
719
  <span class="tot">${f1(e.total)}</span></div></td></tr>`;
720
  }).join("") : `<tr><td colspan="11" class="who">${d.empty}</td></tr>`;
721
 
722
+ wireCopy($("#rows"));
 
 
 
 
 
 
 
 
 
723
 
724
  // rows carry their entry so the overlay never has to look anything up again
725
  [...document.querySelectorAll("#rows tr")].forEach((tr, i) => {
726
  const e = rows[i];
727
  if(!e) return;
728
+ tr.onmouseenter = () => { clearTimeout(OV._t); showOverlay(e, tr.getBoundingClientRect()); };
729
+ // a short grace period, so moving the pointer into the panel does not dismiss it
730
+ tr.onmouseleave = () => { OV._t = setTimeout(hideOverlay, 220); };
731
  // touch has no hover, so a tap toggles it
732
  tr.onclick = () => (OV.style.display === "block" && OV.dataset.id === e.candidate_id)
733
  ? hideOverlay()
 
928
  ? `<div class="oflag">${e.relegate_reason.map(reasonText).join(" / ")}</div>` : "";
929
  const who = e.is_anchor ? (e.note||"") : [e.hf_user, e.model_name].filter(Boolean).join(" 路 ");
930
  const full = (e.visibility === "public" && e.smiles) ? e.smiles : e.smiles_masked;
931
+ const canCopy = e.visibility === "public" && e.smiles;
932
  const smi = e.is_anchor ? ""
933
  : `<div class="osmi">${full || ""}`
934
+ + (canCopy ? `<button class="cp" data-smi="${e.smiles}" title="${d.copy_smiles}">\u29c9</button>` : "")
935
  + (e.visibility === "public" ? "" : `<br>馃敀 ${d.private_full}`) + `</div>`;
936
  return `<h4>${e.candidate_id}</h4><div class="oid">${who}${e.skeleton?" 路 "+e.skeleton:""}</div>`
937
  + rows + pred + smi + flag + `<div class="ofoot">${d.ov_foot}</div>`;
938
  }
939
 
940
+ function wireCopy(root){
941
+ root.querySelectorAll(".cp").forEach(b => b.onclick = async (ev) => {
942
+ // without this the click also reaches the row and toggles the overlay
943
+ ev.stopPropagation();
944
+ const v = b.dataset.smi;
945
+ try{ await navigator.clipboard.writeText(v); }
946
+ catch(_){ const ta=document.createElement("textarea"); ta.value=v;
947
+ document.body.appendChild(ta); ta.select(); document.execCommand("copy"); ta.remove(); }
948
+ b.classList.add("done"); b.textContent = "\u2713";
949
+ setTimeout(()=>{ b.classList.remove("done"); b.textContent = "\u29c9"; }, 1200);
950
+ });
951
+ }
952
+
953
  function showOverlay(e, rect){
954
  OV.innerHTML = overlayHTML(e);
955
  OV.style.display = "block";
 
957
  // flip above the row rather than open past the bottom of the window
958
  let top = rect.bottom + 8;
959
  if(top + h > window.innerHeight - 8) top = Math.max(8, rect.top - h - 8);
960
+ // anchor to the right of the row: the structure and its copy button live on the left,
961
+ // and opening over them buries the control the reader was reaching for
962
+ let left = Math.max(8, Math.min(rect.right - w, window.innerWidth - w - 12));
963
  OV.style.top = top + "px";
964
+ OV.style.left = left + "px";
965
+ wireCopy(OV);
966
  }
967
  function hideOverlay(){ OV.style.display = "none"; }
968
+ OV.onmouseenter = () => clearTimeout(OV._t);
969
+ OV.onmouseleave = hideOverlay;
970
 
971
  async function load(){
972
  try{