diff --git a/app/api.py b/app/api.py index 38ca71b..9fd8677 100644 --- a/app/api.py +++ b/app/api.py @@ -456,6 +456,7 @@ def create_app(db_path, img_dir, state_dir=None): for r in data['items']] data['items'] = items data['mode'] = mode + data['prefs'] = {'rubric_debug': get_pref(conn, 'rubric_debug', 'off')} data['can_forge'] = (mode != 'strict' or secmod.fresh_profile_exists(conn, sec)) data['counts'] = secmod.section_counts(conn, sec) @@ -653,7 +654,8 @@ def create_app(db_path, img_dir, state_dir=None): def set_pref_endpoint(body: dict): key = body.get('key') value = body.get('value') - allowed = {'forge_mode': ('ondemand', 'strict')} + allowed = {'forge_mode': ('ondemand', 'strict'), + 'rubric_debug': ('off', 'on')} if key not in allowed or value not in allowed[key]: raise HTTPException(status_code=400, detail='unknown or invalid pref') diff --git a/app/static/index.html b/app/static/index.html index 64149ac..2fbd725 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -321,6 +321,7 @@ nav{height:calc(68px + env(safe-area-inset-bottom,0px));display:flex;align-items
Forge:on demand⇄strict
+
Why breakdown:off⇄on
@@ -805,7 +806,9 @@ function openDetail(data,ctx){ $('detail').classList.add('on'); $('dback').classList.add('on'); $('dbody').scrollTop=0; } -/* P3 dossier rank block: gauge + points + strongest ± lines + audit footer */ +/* P3 dossier rank block: gauge + points% minimal; the full "why" breakdown + and the audit footer render only when prefs.rubric_debug === 'on' (§10.5). */ +function rubricOn(){ return !!(section&§ion.prefs&§ion.prefs.rubric_debug==='on'); } function fillRank(data){ if(!data||data.tank==null||data.tank.value==null){ $('drank').style.display='none'; @@ -813,21 +816,27 @@ function fillRank(data){ } $('drank').style.display='flex'; $('drankTank').innerHTML=tankSVG(data.tank,true); - $('dpts').textContent=data.tank.value+'/100'; - const lines=(data.points_breakdown||[]).slice() - .sort((a,b)=>Math.abs(b.value)-Math.abs(a.value)).slice(0,4); - $('dlines').innerHTML=lines.map(l=>{ - const lbl= l.source==='agent'?('agent: '+l.label):l.label; - const n= l.value>=0?(' +'+l.value):(' '+l.value); - return `
· ${lbl}${n}
`; - }).join(''); - const ri= section&§ion.read_info; - $('dfoot').textContent= ri?`from ${ri.events} strikes · read ${relTime(ri.profile_at)}`:''; - // list/matches payloads carry no breakdown — fill it on the quiet - if(!data.points_breakdown&&data.id&&!legacyMode){ - getJSON('/api/listing/'+data.id).then(full=>{ - if(full&&full.points_breakdown&&dCtx) fillRank(full); - }); + $('dpts').textContent=(data.tank.value|0)+'%'; + if(rubricOn()){ + const lines=(data.points_breakdown||[]).slice() + .sort((a,b)=>Math.abs(b.value)-Math.abs(a.value)).slice(0,4); + $('dlines').innerHTML=lines.map(l=>{ + const lbl= l.source==='agent'?('agent: '+l.label):l.label; + const n= l.value>=0?(' +'+l.value):(' '+l.value); + return `
· ${lbl}${n}
`; + }).join(''); + const ri= section&§ion.read_info; + $('dfoot').textContent= ri?`from ${ri.events} strikes · read ${relTime(ri.profile_at)}`:''; + // list/matches payloads carry no breakdown — fill it on the quiet + if(!data.points_breakdown&&data.id&&!legacyMode){ + getJSON('/api/listing/'+data.id).then(full=>{ + if(full&&full.points_breakdown&&dCtx) fillRank(full); + }); + } + }else{ + // minimal display: gauge + % only; skip the quiet enrichment fetch + $('dlines').innerHTML=''; + $('dfoot').textContent=''; } } function closeDetail(){ const d=$('detail'); d.style.transition=''; d.style.transform=''; d.classList.remove('on'); $('dback').classList.remove('on'); dCtx=null; dEntry=null; } @@ -1190,6 +1199,7 @@ function updateSectionUI(body){ sectDecided=decided; paintSect(); paintMode(); + paintRubricMode(); const c=body.counts||{}; $('repCounts').innerHTML=`♥ ${c.likes||0} · ★ ${c.maybes||0} · ✕ ${c.nopes||0}`; const hits=[]; @@ -1203,6 +1213,21 @@ async function setMode(m){ } $('modeOnd').onclick=()=>setMode('ondemand'); $('modeStr').onclick=()=>setMode('strict'); +function paintRubricMode(){ + const on=rubricOn(); + $('tRubOff').classList.toggle('on',!on); + $('tRubOn').classList.toggle('on',on); +} +async function setRubricDebug(m){ + const res=await fetch('/api/prefs',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({key:'rubric_debug',value:m})}).catch(()=>null); + if(res&&res.ok){ + if(section){ section.prefs=section.prefs||{}; section.prefs.rubric_debug=m; } + paintRubricMode(); + if(!legacyMode) loadSection(); // repaint any dossier already open + } +} +$('tRubOff').onclick=()=>setRubricDebug('off'); +$('tRubOn').onclick=()=>setRubricDebug('on'); $('btnForge').onclick=async()=>{ if(forgeBusy||!section) return; forgeBusy=true;