feat(v1.1): item 4 — minimal rubric (gauge + %) and rubric_debug 'why' toggle
- prefs API accepts the new rubric_debug key (off|on), the only new runtime write in v1.1; every section_state body now carries prefs.rubric_debug. - fillRank renders the tank gauge + rounded % by default; the breakdown lines, audit footer and the quiet listing-enrichment fetch run only when the pref is on. List rows and the card face stay rubric-free (unchanged). - Trends 'Actions' gains a 'Why breakdown: off | on' row in the setMode style; updateSectionUI repaints it on every section hydrate.
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -321,6 +321,7 @@ nav{height:calc(68px + env(safe-area-inset-bottom,0px));display:flex;align-items
|
||||
<div class="tstatus" id="runStatus"></div>
|
||||
</div>
|
||||
<div class="trow"><span>Forge:</span><span id="tOnd" class="on">on demand</span><i>⇄</i><span id="tStr">strict</span></div>
|
||||
<div class="trow"><span>Why breakdown:</span><span id="tRubOff" class="on">off</span><i>⇄</i><span id="tRubOn">on</span></div>
|
||||
<div class="tmuted" id="tLastRun"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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 `<div class="${l.value>=0?'pos':'neg'}">· ${lbl}${n}</div>`;
|
||||
}).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 `<div class="${l.value>=0?'pos':'neg'}">· ${lbl}${n}</div>`;
|
||||
}).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;
|
||||
|
||||
Reference in New Issue
Block a user