Spaces:
Running
Running
index.html: landing + deep links
Browse files- index.html +33 -322
index.html
CHANGED
|
@@ -1,326 +1,37 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html lang="en">
|
|
|
|
|
|
|
| 3 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 4 |
-
<title>
|
| 5 |
<style>
|
| 6 |
-
:root{
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
.hrow{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:10px 20px}
|
| 20 |
-
header h1{font-size:14px;margin:0;letter-spacing:.2px;font-weight:800}
|
| 21 |
-
.sub{font-size:12.5px;color:var(--mut)}
|
| 22 |
-
#bar{height:5px;background:var(--line);border-radius:3px;margin:2px 0}
|
| 23 |
-
#bar>i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--primary),#22A55B);border-radius:3px;transition:width .25s}
|
| 24 |
-
main{max-width:1120px;margin:0 auto;padding:18px 20px 110px;display:grid;grid-template-columns:1fr 356px;gap:22px;align-items:start}
|
| 25 |
-
@media(max-width:920px){main{grid-template-columns:1fr}}
|
| 26 |
-
.card{background:var(--card);border:1px solid var(--line);border-radius:16px;box-shadow:0 1px 2px rgba(33,29,26,.05),0 10px 28px rgba(33,29,26,.05)}
|
| 27 |
-
.passage-card{padding:16px 20px 14px}
|
| 28 |
-
.grid-2{display:grid;grid-template-columns:auto 1fr;gap:18px}
|
| 29 |
-
.badges{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:10px}
|
| 30 |
-
.badge{font-size:11px;font-weight:700;padding:3px 10px;border-radius:999px;background:var(--unsc-bg);color:var(--unsc)}
|
| 31 |
-
.b-keep{background:var(--keep-bg);color:var(--keep)}.b-drop{background:var(--drop-bg);color:var(--drop)}
|
| 32 |
-
.b-confusion{background:var(--conf-bg);color:var(--conf)}.b-mixed{background:#E9E6FF;color:#4338CA}.b-unscored{background:var(--unsc-bg);color:var(--unsc)}
|
| 33 |
-
.passage{max-height:min(54vh,560px);overflow-y:auto;font-size:14.5px;line-height:1.8;padding:14px 6px;border:1px solid var(--line);border-radius:12px;background:#FDFCFA}
|
| 34 |
-
.passage mark{cursor:pointer;border-radius:3px;padding:0 2px;font-weight:600}
|
| 35 |
-
mark.m-u{background:var(--mark-u);border-bottom:2px solid var(--mark-u-line)}
|
| 36 |
-
mark.m-k{background:var(--mark-k);border-bottom:2px solid var(--mark-k-line)}
|
| 37 |
-
mark.m-d{background:var(--mark-d);border-bottom:2px solid var(--mark-d-line)}
|
| 38 |
-
mark.on{outline:2px solid var(--primary);outline-offset:1px}
|
| 39 |
-
mark sup{font-size:9.5px;font-weight:800;color:var(--primary);margin-left:1px}
|
| 40 |
-
.legend{display:flex;gap:8px;align-items:center;margin-top:8px;font-size:11.5px;color:var(--mut);flex-wrap:wrap}
|
| 41 |
-
.legend i{display:inline-block;width:20px;height:4px;border-radius:2px}
|
| 42 |
-
.legend .u i{background:var(--mark-u);border-bottom:2px solid var(--mark-u-line)}
|
| 43 |
-
.legend .k i{background:var(--mark-k);border-bottom:2px solid var(--mark-k-line)}
|
| 44 |
-
.legend .d i{background:var(--mark-d);border-bottom:2px solid var(--mark-d-line)}
|
| 45 |
-
.legend .a i{outline:2px solid var(--primary);outline-offset:1px}
|
| 46 |
-
.chips{display:flex;gap:7px;align-items:center;overflow-x:auto;margin-top:10px;padding:7px 4px;scrollbar-width:thin}
|
| 47 |
-
.chips::-webkit-scrollbar{height:5px}.chips::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px}
|
| 48 |
-
.chip{font:inherit;font-size:11.5px;cursor:pointer;border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:999px;padding:5px 11px;white-space:nowrap;display:inline-flex;gap:5px;align-items:center;transition:all .15s}
|
| 49 |
-
.chip b{width:18px;height:18px;border-radius:50%;font-size:10px;display:inline-grid;place-items:center;color:#fff}
|
| 50 |
-
.chip .dot{width:7px;height:7px;border-radius:50%;background:var(--conf-line)}
|
| 51 |
-
.chip.r-k{background:var(--keep-bg);border-color:var(--keep-line);color:var(--keep)}
|
| 52 |
-
.chip.r-d{background:var(--drop-bg);border-color:var(--drop-line);color:var(--drop)}
|
| 53 |
-
.chip.on{outline:2px solid var(--primary);outline-offset:1px}
|
| 54 |
-
.chip .sc{font-variant-numeric:tabular-nums}
|
| 55 |
-
.panel-card{padding:16px 18px;position:sticky;top:96px;display:flex;flex-direction:column;gap:12px}
|
| 56 |
-
.panel-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.8px;color:var(--mut);font-weight:800}
|
| 57 |
-
.surface{font-size:20px;font-weight:800;line-height:1.35;padding:10px 12px;background:#FBF8F1;border:1px solid #EADFC4;border-radius:12px}
|
| 58 |
-
.pstate{font-size:13px;font-weight:700}
|
| 59 |
-
.pstate.un{color:var(--conf)}.pstate.k{color:var(--keep)}.pstate.d{color:var(--drop)}
|
| 60 |
-
.scoreline{display:flex;gap:7px;align-items:center;flex-wrap:wrap;font-size:12.5px;color:var(--mut)}
|
| 61 |
-
.tag{font-size:11px;font-weight:800;padding:2px 9px;border-radius:999px;letter-spacing:.3px}
|
| 62 |
-
.tag.k{background:var(--keep-bg);color:var(--keep)}.tag.d{background:var(--drop-bg);color:var(--drop)}
|
| 63 |
-
.tag.c{background:var(--conf-bg);color:var(--conf)}.tag.u{background:var(--unsc-bg);color:var(--unsc)}
|
| 64 |
-
.note{font:inherit;font-size:13.5px;padding:9px 12px;border:1px solid var(--line);border-radius:10px;background:#FDFCFA;width:100%}
|
| 65 |
-
.note:focus{outline:2px solid var(--primary);outline-offset:0;border-color:var(--primary)}
|
| 66 |
-
.hint{font-size:11.5px;color:var(--mut);line-height:1.55}
|
| 67 |
-
kbd{font-family:inherit;font-size:10.5px;background:var(--unsc-bg);border:1px solid var(--line);border-radius:5px;padding:1px 5px}
|
| 68 |
-
#dock{position:fixed;left:0;right:0;bottom:0;z-index:30;background:rgba(255,255,255,.97);backdrop-filter:blur(6px);border-top:1px solid var(--line);padding:11px 20px;display:flex;gap:8px;align-items:center;justify-content:center;flex-wrap:wrap}
|
| 69 |
-
button{font:inherit;cursor:pointer;border:1px solid var(--line);background:var(--card);color:var(--ink);border-radius:12px;padding:10px 18px;font-size:13.5px;transition:all .15s}
|
| 70 |
-
button.nav:hover{border-color:var(--primary);color:var(--primary)}
|
| 71 |
-
button:disabled{opacity:.4;cursor:default}
|
| 72 |
-
button.keep{background:var(--keep);border-color:var(--keep);color:#fff;font-weight:800;min-width:132px;box-shadow:0 1px 3px rgba(15,122,77,.35)}
|
| 73 |
-
button.keep:hover{filter:brightness(1.05)}
|
| 74 |
-
button.drop{background:var(--drop);border-color:var(--drop);color:#fff;font-weight:800;min-width:132px;box-shadow:0 1px 3px rgba(194,65,43,.35)}
|
| 75 |
-
button.drop:hover{filter:brightness(1.05)}
|
| 76 |
-
button.primary{background:var(--primary);border-color:var(--primary);color:#fff}
|
| 77 |
-
select,input[type=text]{font:inherit;font-size:12.5px;padding:6px 10px;border:1px solid var(--line);border-radius:9px;background:var(--card);color:var(--ink)}
|
| 78 |
-
.state{font-size:12.5px;color:var(--mut);min-width:180px;text-align:center;font-variant-numeric:tabular-nums}
|
| 79 |
-
#who{width:76px}
|
| 80 |
-
.btnrow{display:flex;gap:8px;align-items:center}
|
| 81 |
-
.foot{text-align:center;color:var(--mut);font-size:12px;padding-top:14px}
|
| 82 |
-
.empty{text-align:center;color:var(--mut);padding:60px 0;font-size:16px}
|
| 83 |
-
.warn{background:#FBF0DC;border:1px solid var(--conf-line);border-radius:10px;padding:8px 12px;font-size:12.5px}
|
| 84 |
-
</style></head><body>
|
| 85 |
-
<header>
|
| 86 |
-
<div class="hrow">
|
| 87 |
-
<h1>datause-ner labeling</h1>
|
| 88 |
-
<span class="sub" id="qname">queue</span>
|
| 89 |
-
<span class="sub" id="prog" style="font-weight:800;color:var(--ink)"></span><span style="flex:1"></span>
|
| 90 |
-
<label class="sub">annotator <input type="text" id="who" placeholder="initials" style="width:76px"></label>
|
| 91 |
-
<label class="sub"><input type="checkbox" id="rev"> scores</label>
|
| 92 |
-
<select id="fQueue" title="queue"></select>
|
| 93 |
-
<select id="fOrigin" title="origin filter"></select>
|
| 94 |
-
<select id="fBand" title="band filter"></select>
|
| 95 |
-
<select id="fLuna" title="Luna verdict vs model">
|
| 96 |
-
<option value="">luna×model: all</option><option value="agree">agree</option>
|
| 97 |
-
<option value="conflict">conflict</option><option value="unsure">model unsure</option></select>
|
| 98 |
-
<button class="primary" onclick="exportRulings()">⬇ Export</button>
|
| 99 |
-
<span class="sub" id="sink"></span>
|
| 100 |
-
</div>
|
| 101 |
-
<div id="bar"><i></i></div>
|
| 102 |
-
</header>
|
| 103 |
<main>
|
| 104 |
-
<
|
| 105 |
-
<
|
| 106 |
-
<div
|
| 107 |
-
<
|
| 108 |
-
<
|
| 109 |
-
<
|
| 110 |
-
</
|
| 111 |
-
<
|
| 112 |
-
<div
|
| 113 |
-
</
|
| 114 |
-
<
|
| 115 |
-
<
|
| 116 |
-
<
|
| 117 |
-
<div class="scoreline"><span id="pos"></span><span class="sc" id="sc"></span></div>
|
| 118 |
-
<div class="pstate" id="pstate">unruled</div>
|
| 119 |
-
<div class="btnrow">
|
| 120 |
-
<button class="keep" id="bKeep" onclick="rule('DATA_MENTION')">✓ Keep</button>
|
| 121 |
-
<button class="drop" id="bDrop" onclick="rule('NON_MENTION')">✕ Drop</button>
|
| 122 |
-
</div>
|
| 123 |
-
<input class="note" id="note" placeholder="note (optional) — saved with the ruling">
|
| 124 |
-
<div class="hint"><kbd>k</kbd> keep · <kbd>d</kbd> drop · <kbd>←</kbd><kbd>→</kbd> mention · <kbd>↑</kbd><kbd>↓</kbd> passage · <kbd>s</kbd> skip · <kbd>z</kbd> undo</div>
|
| 125 |
-
</div>
|
| 126 |
-
<div class="empty" id="empty" style="display:none">Nothing matches the active filters.</div>
|
| 127 |
</main>
|
| 128 |
-
<
|
| 129 |
-
<
|
| 130 |
-
<button class="nav" id="bMPrev" onclick="moveM(-1)">‹ M <span id="mPos"></span></button>
|
| 131 |
-
<button class="keep" onclick="rule('DATA_MENTION')">✓ Keep</button>
|
| 132 |
-
<button class="drop" onclick="rule('NON_MENTION')">✕ Drop</button>
|
| 133 |
-
<button class="nav" id="bMNext" onclick="moveM(1)"><span id="mPos2"></span> M ›</button>
|
| 134 |
-
<button class="nav" id="bPNext" onclick="moveP(1)">P ⏭</button>
|
| 135 |
-
<button class="nav" onclick="moveP(1)">Skip <small>(s)</small></button>
|
| 136 |
-
<button class="nav" id="bUndo" onclick="undo()">⟲ Undo</button>
|
| 137 |
-
<span class="state" id="state"></span>
|
| 138 |
-
</div>
|
| 139 |
-
<script>
|
| 140 |
-
let Q=[], R={}, pidx=0, mi=0, lastRuled=null;
|
| 141 |
-
const $=id=>document.getElementById(id);
|
| 142 |
-
const LS='datause_ner_rulings_v1';
|
| 143 |
-
try{R=JSON.parse(localStorage.getItem(LS)||'{}')}catch(e){R={}}
|
| 144 |
-
$('who').value=localStorage.getItem('datause_ner_who')||'';
|
| 145 |
-
$('who').oninput=()=>localStorage.setItem('datause_ner_who',$('who').value);
|
| 146 |
-
const save=()=>localStorage.setItem(LS,JSON.stringify(R));
|
| 147 |
-
const esc=s=>String(s??'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
| 148 |
-
const mentionsOf=q=>Array.isArray(q.mentions)&&q.mentions.length?q.mentions:[{
|
| 149 |
-
key:q.key,surface:q.surface,start:q.start,end:q.end,
|
| 150 |
-
head_score:q.head_score,band:q.band}];
|
| 151 |
-
const isFlat=q=>!Array.isArray(q.mentions);
|
| 152 |
-
async function post(ruling){
|
| 153 |
-
try{const r=await fetch('/api/rulings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(ruling)});
|
| 154 |
-
if(r.ok){$('sink').textContent='● server ('+(await r.json()).n+')';return}}catch(e){}
|
| 155 |
-
$('sink').textContent='● local export';
|
| 156 |
-
}
|
| 157 |
-
async function load(){
|
| 158 |
-
try{const r=await fetch('/api/queue');if(r.ok)Q=(await r.json()).items||[]}catch(e){}
|
| 159 |
-
if(!Q.length){
|
| 160 |
-
for(const f of ['./queue_gliner.json']){
|
| 161 |
-
try{const r=await fetch(f);if(r.ok)Q.push(...(await r.text()).split('\n').filter(Boolean).map(JSON.parse))}catch(e){}
|
| 162 |
-
}
|
| 163 |
-
}
|
| 164 |
-
fillFilters();render();
|
| 165 |
-
}
|
| 166 |
-
const lunaState=m=>{
|
| 167 |
-
if(m.luna==null||m.head_score==null)return null;
|
| 168 |
-
if(m.luna_split)return 'conflict'; // Luna itself split on the same span
|
| 169 |
-
if(m.band==='keep')return m.luna===1?'agree':'conflict';
|
| 170 |
-
if(m.band==='drop')return m.luna===0?'agree':'conflict';
|
| 171 |
-
return 'unsure';
|
| 172 |
-
};
|
| 173 |
-
const filtered=()=>{const qu=$('fQueue').value,o=$('fOrigin').value,b=$('fBand').value,lu=$('fLuna').value;
|
| 174 |
-
return Q.map((q,i)=>({q,i})).filter(({q})=>(!qu||q.queue===qu)&&(!o||q.origin===o)&&(!b||q.band===b)
|
| 175 |
-
&&(!lu||mentionsOf(q).some(m=>lunaState(m)===lu)))};
|
| 176 |
-
const totalMentions=()=>Q.reduce((n,q)=>n+(q.mentions?.length||1),0);
|
| 177 |
-
function fillFilters(){
|
| 178 |
-
const qs=[...new Set(Q.map(q=>q.queue))].sort();
|
| 179 |
-
$('fQueue').innerHTML=qs.map(q=>`<option value="${esc(q)}">${esc(q)} · ${Q.filter(x=>x.queue===q).length}</option>`).join('');
|
| 180 |
-
if(qs.includes('gliner'))$('fQueue').value='gliner';
|
| 181 |
-
else if(qs.includes('probe_candidates/pool'))$('fQueue').value='probe_candidates/pool';
|
| 182 |
-
else if(qs.includes('human473'))$('fQueue').value='human473';
|
| 183 |
-
const os=[...new Set(Q.map(q=>q.origin))].sort(),bs=[...new Set(Q.map(q=>q.band))].sort();
|
| 184 |
-
$('fOrigin').innerHTML='<option value="">all origins</option>'+os.map(o=>`<option>${esc(o)}</option>`).join('');
|
| 185 |
-
$('fBand').innerHTML='<option value="">all bands</option>'+bs.map(b=>`<option>${esc(b)}</option>`).join('');
|
| 186 |
-
for(const id of ['fQueue','fOrigin','fBand','fLuna']) $(id).onchange=()=>{pidx=0;mi=0;render()};
|
| 187 |
-
$('rev').onchange=()=>render();
|
| 188 |
-
}
|
| 189 |
-
function bandsHTML(m){
|
| 190 |
-
const t=m.band==='keep'?'k':m.band==='drop'?'d':m.band==='unscored'?'u':'c';
|
| 191 |
-
return `<span class="tag ${t}">${esc(m.band||'?')}</span>`;
|
| 192 |
-
}
|
| 193 |
-
function contextHTML(q){
|
| 194 |
-
const p=q.ctx||'';const ms=mentionsOf(q);
|
| 195 |
-
const marks=[];
|
| 196 |
-
for(let i=0;i<ms.length;i++){
|
| 197 |
-
const m=ms[i];let s=m.start,e=m.end;
|
| 198 |
-
if(!(Number.isInteger(s)&&Number.isInteger(e)&&e>s&&p.slice(s,e)===m.surface)){
|
| 199 |
-
s=p.indexOf(m.surface||'');e=s<0?0:s+(m.surface||'').length;
|
| 200 |
-
}
|
| 201 |
-
if(s<0)continue;
|
| 202 |
-
marks.push({s,e,i});
|
| 203 |
-
}
|
| 204 |
-
// char-once walk: overlapping spans never duplicate passage text; a mark
|
| 205 |
-
// starting inside the previous one is suppressed in-text (chip remains)
|
| 206 |
-
marks.sort((a,b)=>a.s-b.s||b.e-a.e);
|
| 207 |
-
let out='',cur=0,suppressed=0;
|
| 208 |
-
for(const mk of marks){
|
| 209 |
-
if(mk.s<cur){suppressed++;continue}
|
| 210 |
-
out+=esc(p.slice(cur,mk.s));
|
| 211 |
-
const r=R[ms[mk.i].key];
|
| 212 |
-
const cls=r? (r.ruling==='DATA_MENTION'?'m-k':'m-d') : 'm-u';
|
| 213 |
-
out+=`<mark class="${cls}" data-mi="${mk.i}" onclick="clickM(${mk.i})">${esc(p.slice(mk.s,mk.e))}${ms.length>1?`<sup>${mk.i+1}</sup>`:''}</mark>`;
|
| 214 |
-
cur=mk.e;
|
| 215 |
-
}
|
| 216 |
-
out+=esc(p.slice(cur));
|
| 217 |
-
return out;
|
| 218 |
-
}
|
| 219 |
-
function render(){
|
| 220 |
-
const F=filtered();
|
| 221 |
-
const ruled=Object.keys(R).length, tm=totalMentions();
|
| 222 |
-
$('prog').textContent=`${ruled}/${tm} ruled`;
|
| 223 |
-
$('qname').textContent=`${$('fQueue').value||'queue'} · ${F.length}`;
|
| 224 |
-
$('empty').style.display=F.length?'none':'block';
|
| 225 |
-
const bar=document.querySelector('#bar>i');bar.style.width=(tm?100*ruled/tm:0)+'%';
|
| 226 |
-
if(!F.length){$('passage').innerHTML='';$('chips').innerHTML='';$('state').textContent='adjust filters';return}
|
| 227 |
-
pidx=Math.max(0,Math.min(pidx,F.length-1));
|
| 228 |
-
const q=F[pidx].q, ms=mentionsOf(q);
|
| 229 |
-
mi=Math.max(0,Math.min(mi,ms.length-1));
|
| 230 |
-
const m=ms[mi],r=R[m.key];
|
| 231 |
-
$('bOrigin').textContent=[q.origin,q.subset].filter(Boolean).join(' · ');
|
| 232 |
-
const nk=ms.filter(x=>x.luna===1).length,nd=ms.filter(x=>x.luna===0).length;
|
| 233 |
-
$('bQueue').textContent=(isFlat(q)?'single mention':`${q.mentions.length} mentions`)
|
| 234 |
-
+(nk+nd?` · luna: ${nk}✓ / ${nd}✕`:'');
|
| 235 |
-
const bb=$('bBand');bb.textContent=`tag: ${q.band}`;bb.className='badge b-'+(q.band||'unscored');
|
| 236 |
-
$('bKey').textContent=q.mentions?m.key:q.key;
|
| 237 |
-
$('noctx').style.display=q.ctx?'none':'block';
|
| 238 |
-
$('passage').innerHTML=q.ctx?contextHTML(q):`<div class="empty">${esc(m.surface||'(no context)')}</div>`;
|
| 239 |
-
for(const el of $('passage').querySelectorAll('mark[data-mi]')){
|
| 240 |
-
if(el.dataset.mi===String(mi))el.classList.add('on');
|
| 241 |
-
else el.classList.remove('on');
|
| 242 |
-
}
|
| 243 |
-
$('passage').style.display='';
|
| 244 |
-
const act=$('passage').querySelector('mark.on');
|
| 245 |
-
if(act)act.scrollIntoView({block:'center',behavior:'smooth'});
|
| 246 |
-
$('chips').innerHTML=ms.map((mm,i)=>{
|
| 247 |
-
const rr=R[mm.key];
|
| 248 |
-
const cls=rr?(rr.ruling==='DATA_MENTION'?'r-k':'r-d'):'';
|
| 249 |
-
const on=i===mi?'on':'';
|
| 250 |
-
const dot=mm.luna_split?'var(--conf)':mm.luna===1?'var(--keep)':mm.luna===0?'var(--drop)':'var(--conf-line)';
|
| 251 |
-
return `<button class="chip ${cls} ${on}" data-ma="${i}" onclick="clickM(${i})"><b>${i+1}</b><span class="dot" style="background:${dot}"></span>${esc(mm.surface)}</button>`;
|
| 252 |
-
}).join('');
|
| 253 |
-
const actChip=$('chips').querySelector('button.on');
|
| 254 |
-
if(actChip)actChip.scrollIntoView({inline:'center',behavior:'smooth',block:'nearest'});
|
| 255 |
-
const sc=$('rev').checked?(m.head_score==null?'n/a':(+m.head_score).toFixed(3)):'•••';
|
| 256 |
-
$('surface').textContent=m.surface||'(no surface)';
|
| 257 |
-
$('pos').textContent=`M ${mi+1}/${ms.length}`;
|
| 258 |
-
$('sc').innerHTML=bandsHTML(m)
|
| 259 |
-
+(m.luna_split?`<span class="tag c">Luna split ✓/✕</span>`
|
| 260 |
-
:m.luna!=null?`<span class="tag ${m.luna===1?'k':'d'}">Luna ${m.luna===1?'✓ kept':'✕ dropped'}</span>`:'')
|
| 261 |
-
+`<span class="sc">probe ${sc}</span>`
|
| 262 |
-
+(m.extractor_score!=null?`<span class="sc">ext ${(+m.extractor_score).toFixed(2)}</span>`:'');
|
| 263 |
-
const st=$('pstate');
|
| 264 |
-
if(r){st.textContent=(r.ruling==='DATA_MENTION'?'✓ kept':'✕ dropped')+' — your ruling';st.className='pstate '+(r.ruling==='DATA_MENTION'?'k':'d')}
|
| 265 |
-
else{st.textContent='unruled — model tag above is a hint, you decide';st.className='pstate un'}
|
| 266 |
-
$('note').value=r?.note||'';
|
| 267 |
-
$('state').textContent=`P ${pidx+1}/${F.length} · M ${mi+1}/${ms.length}${r?' · ruled':''}`;
|
| 268 |
-
$('mPos').textContent=`${mi+1}/${ms.length}`;$('mPos2').textContent=`${mi+1}/${ms.length}`;
|
| 269 |
-
$('bPPrev').disabled=pidx===0;$('bPNext').disabled=pidx>=F.length-1;
|
| 270 |
-
$('bMPrev').disabled=mi===0&&pidx===0;$('bMNext').disabled=mi>=ms.length-1&&pidx>=F.length-1;
|
| 271 |
-
$('bUndo').disabled=!lastRuled;
|
| 272 |
-
}
|
| 273 |
-
function clickM(i){mi=i;render()}
|
| 274 |
-
function moveM(d){
|
| 275 |
-
const F=filtered();if(!F.length)return;
|
| 276 |
-
const ms=mentionsOf(F[pidx].q);
|
| 277 |
-
if(d>0){if(mi<ms.length-1)mi++;else if(pidx<F.length-1){pidx++;mi=0} }
|
| 278 |
-
else{if(mi>0)mi--;else if(pidx>0){pidx--;mi=mentionsOf(F[pidx].q).length-1} }
|
| 279 |
-
render();
|
| 280 |
-
}
|
| 281 |
-
function moveP(d){
|
| 282 |
-
const F=filtered();if(!F.length)return;
|
| 283 |
-
pidx=Math.max(0,Math.min(pidx+d,F.length-1));mi=0;render();
|
| 284 |
-
}
|
| 285 |
-
async function rule(v){
|
| 286 |
-
const F=filtered();if(!F.length)return;
|
| 287 |
-
const q=F[pidx].q,ms=mentionsOf(q),m=ms[mi];
|
| 288 |
-
R[m.key]={key:m.key,ruling:v,annotator:$('who').value||'anon',ts:new Date().toISOString(),
|
| 289 |
-
note:$('note').value||'',head_score:m.head_score,origin:q.origin,surface:m.surface,queue:q.queue};
|
| 290 |
-
lastRuled=m.key;save();post(R[m.key]);
|
| 291 |
-
if(mi<ms.length-1)mi++;
|
| 292 |
-
else if(pidx<F.length-1){pidx++;mi=0}
|
| 293 |
-
render();
|
| 294 |
-
}
|
| 295 |
-
function undo(){
|
| 296 |
-
if(!lastRuled)return;
|
| 297 |
-
const F=filtered();
|
| 298 |
-
let found=false;
|
| 299 |
-
for(let fi=0;fi<F.length;fi++){
|
| 300 |
-
const ms=mentionsOf(F[fi].q);
|
| 301 |
-
for(let j=0;j<ms.length;j++){
|
| 302 |
-
if(ms[j].key===lastRuled){pidx=fi;mi=j;found=true;break}
|
| 303 |
-
}
|
| 304 |
-
if(found)break;
|
| 305 |
-
}
|
| 306 |
-
delete R[lastRuled];lastRuled=null;save();
|
| 307 |
-
if(!found){$('state').textContent='ruled item outside current filters';return}
|
| 308 |
-
render();
|
| 309 |
-
}
|
| 310 |
-
function exportRulings(){
|
| 311 |
-
const blob=new Blob([JSON.stringify({items:Object.values(R)},null,1)],{type:'application/json'});
|
| 312 |
-
const a=document.createElement('a');a.href=URL.createObjectURL(blob);
|
| 313 |
-
a.download=`rulings-${$('who').value||'anon'}-export.json`;a.click();
|
| 314 |
-
}
|
| 315 |
-
document.addEventListener('keydown',e=>{
|
| 316 |
-
if(/INPUT|SELECT|TEXTAREA/.test(e.target.tagName))return;
|
| 317 |
-
if(e.key==='k')rule('DATA_MENTION');
|
| 318 |
-
else if(e.key==='d')rule('NON_MENTION');
|
| 319 |
-
else if(e.key==='ArrowRight')moveM(1);
|
| 320 |
-
else if(e.key==='ArrowLeft')moveM(-1);
|
| 321 |
-
else if(e.key==='ArrowDown'||e.key==='s')moveP(1);
|
| 322 |
-
else if(e.key==='ArrowUp')moveP(-1);
|
| 323 |
-
else if(e.key==='z')undo();
|
| 324 |
-
});
|
| 325 |
-
load();
|
| 326 |
-
</script></body></html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>data-use annotation</title>
|
| 7 |
<style>
|
| 8 |
+
:root { --bg: #f6f8fa; --card: #fff; --fg: #1f2328; --mut: #59636e; --line: #d0d7de; --hl: #0969da; }
|
| 9 |
+
body { background: var(--bg); color: var(--fg); font: 16px/1.6 -apple-system, system-ui, sans-serif; margin: 0; }
|
| 10 |
+
main { max-width: 640px; margin: 0 auto; padding: 60px 20px; text-align: center; }
|
| 11 |
+
h1 { font-size: 24px; margin-bottom: 6px; }
|
| 12 |
+
p { color: var(--mut); }
|
| 13 |
+
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 28px 0; }
|
| 14 |
+
a.user { background: var(--card); border: 2px solid var(--line); border-radius: 12px; padding: 22px 10px; font-size: 19px; font-weight: 700; color: var(--fg); text-decoration: none; }
|
| 15 |
+
a.user:hover { border-color: var(--hl); }
|
| 16 |
+
a.user span { display: block; font-size: 13px; font-weight: 400; color: var(--mut); margin-top: 4px; }
|
| 17 |
+
.links a { color: var(--mut); font-size: 14px; margin: 0 10px; }
|
| 18 |
+
</style>
|
| 19 |
+
</head>
|
| 20 |
+
<body>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
<main>
|
| 22 |
+
<h1>data-use annotation</h1>
|
| 23 |
+
<p>keep / drop data mentions · pick your name to load your 240 passages</p>
|
| 24 |
+
<div class="grid">
|
| 25 |
+
<a class="user" href="./review.html?user=paddy">paddy<span>240 passages</span></a>
|
| 26 |
+
<a class="user" href="./review.html?user=aj">aj<span>240 passages</span></a>
|
| 27 |
+
<a class="user" href="./review.html?user=aivin">aivin<span>240 passages</span></a>
|
| 28 |
+
<a class="user" href="./review.html?user=rafael">rafael<span>240 passages</span></a>
|
| 29 |
+
</div>
|
| 30 |
+
<div class="links">
|
| 31 |
+
<a href="./review.html?ds=sample">sample · 60</a>
|
| 32 |
+
<a href="./review.html?ds=gold">gold · 473</a>
|
| 33 |
+
<a href="./legacy-index.html">legacy UI</a>
|
| 34 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
</main>
|
| 36 |
+
</body>
|
| 37 |
+
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|