← back to Rentv Sheet Enrich Refine
rentv :9797 legend: Cody fixes — sticky header (✕ stays visible on scroll) + Escape cancels a rename
72a7312800a2bb9e553a04185f5ade8a093f287a · 2026-08-13 20:15:36 -0700 · Steve Abrams
- .lghead now position:sticky + ✕ floats within it, so the close button no longer scrolls
out of view on tabs with many colors
- Escape sets a _legEsc flag before closeLegend so the blur handler does NOT commit a
half-typed rename (Escape = cancel, not save)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 72a7312800a2bb9e553a04185f5ade8a093f287a
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 13 20:15:36 2026 -0700
rentv :9797 legend: Cody fixes — sticky header (✕ stays visible on scroll) + Escape cancels a rename
- .lghead now position:sticky + ✕ floats within it, so the close button no longer scrolls
out of view on tabs with many colors
- Escape sets a _legEsc flag before closeLegend so the blur handler does NOT commit a
half-typed rename (Escape = cancel, not save)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public_live/index.html | 31 ++++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
diff --git a/public_live/index.html b/public_live/index.html
index d4bcce7..47f4166 100644
--- a/public_live/index.html
+++ b/public_live/index.html
@@ -91,7 +91,7 @@
td.xcol .linkval,td.xcol .cicon{color:var(--xtext)!important} /* exact-color cell: keep link/icon text readable on the painted bg */
/* color key / legend panel (exact sheet colors -> provisional meanings) */
.legendpanel{position:fixed;right:14px;top:52px;z-index:80;background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 34px #000a;padding:8px;min-width:290px;max-width:380px;max-height:74vh;overflow:auto;display:none;font-size:12px}
- .legendpanel .lghead{font-weight:700;color:var(--dim);padding:2px 4px 8px;border-bottom:1px solid var(--line);margin-bottom:6px}
+ .legendpanel .lghead{font-weight:700;color:var(--dim);padding:2px 4px 8px;border-bottom:1px solid var(--line);margin-bottom:6px;position:sticky;top:-8px;background:var(--panel);z-index:2}
.legendpanel .lgnote{font-weight:400;font-size:10px;display:block;margin-top:2px;color:var(--dim)}
.legendpanel .lgempty{color:var(--dim);padding:8px 4px}
.legendpanel .lgrow{display:flex;align-items:center;gap:8px;padding:3px 4px;border-radius:6px}
@@ -101,7 +101,7 @@
.legendpanel .lgname:focus{box-shadow:inset 0 0 0 2px var(--accent)}
.legendpanel .lgname.lgguess{opacity:.72;font-style:italic}
.legendpanel .lgq{color:var(--dim);font-weight:700;font-size:11px;flex:0 0 auto}
- .legendpanel .lgclose{position:absolute;top:6px;right:8px;background:transparent;border:0;color:var(--dim);cursor:pointer;font-size:14px;line-height:1;padding:2px 4px;z-index:1}
+ .legendpanel .lgclose{float:right;background:transparent;border:0;color:var(--dim);cursor:pointer;font-size:14px;line-height:1;padding:0 2px;margin:-1px -2px 0 8px}
.legendpanel .lgclose:hover{color:var(--ink)}
.legendpanel .lgreset{background:transparent;border:0;color:var(--dim);cursor:pointer;font-size:12px;padding:0 3px;flex:0 0 auto}
.legendpanel .lgreset:hover{color:var(--accent)}
@@ -128,6 +128,9 @@
/* color-coded Status field + click-to-set picker */
td.statuscell{cursor:pointer} td.statuscell:hover{background:var(--rowhover)}
.statuschip{display:inline-block;max-width:100%;padding:2px 10px;border-radius:12px;font-size:.92em;font-weight:600;line-height:1.55;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+ /* derived 🔥 Warmth column — read-only heat pill */
+ td.warmthcell{text-align:center;cursor:default}
+ .heatpill{display:inline-block;min-width:34px;padding:1px 9px;border-radius:11px;font-weight:700;font-variant-numeric:tabular-nums}
td.statuscell.empty{color:var(--dim);font-size:.9em}
td.statuscell .chipdate{display:block;margin-top:3px;color:var(--dim);font-size:.72em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-variant-numeric:tabular-nums}
.statusmenu{position:fixed;z-index:70;background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:0 12px 34px #000a;padding:5px;min-width:230px;max-height:70vh;overflow:auto}
@@ -293,10 +296,12 @@ async function loadData(){
o.splice(o.indexOf(nc)+1,0,fn,ln); ORDER=o;
}
}
- // restore saved sort for THIS view; else default to Last Name ascending
+ // restore saved sort for THIS view; else default to 🔥 Warmth desc (work-first), then Last Name asc
const savedSort=LS.get('sort.'+K, undefined);
if(savedSort!==undefined){ SORT=savedSort; }
- else { const lnIdx=DATA.headers.findIndex(h=>/^last name$/i.test(String(h).trim())); SORT = lnIdx>=0 ? {col:lnIdx, dir:1} : null; }
+ else { const wIdx=DATA.headers.findIndex(h=>String(h).trim()==='🔥 Warmth');
+ if(wIdx>=0){ SORT={col:wIdx, dir:-1}; }
+ else { const lnIdx=DATA.headers.findIndex(h=>/^last name$/i.test(String(h).trim())); SORT = lnIdx>=0 ? {col:lnIdx, dir:1} : null; } }
// restore saved density
const dens=LS.get('dens',null); if(dens) document.documentElement.style.setProperty('--fs',dens+'px');
buildRail(); render(); setSync('ok');
@@ -316,8 +321,10 @@ function passes(r){
return true;
}
function filtered(){ let rows=DATA.rows.map((r,i)=>[r,i]).filter(([r])=>passes(r));
- if(SORT){ const ci=SORT.col,dir=SORT.dir; rows.sort((a,b)=>{const x=g(a[0],ci).toLowerCase(),y=g(b[0],ci).toLowerCase();
+ if(SORT){ const ci=SORT.col,dir=SORT.dir; rows.sort((a,b)=>{const rx=g(a[0],ci),ry=g(b[0],ci),x=rx.toLowerCase(),y=ry.toLowerCase();
if(!x&&!y)return 0; if(!x)return 1; if(!y)return -1; // blanks always sink to the bottom
+ const nx=Number(rx),ny=Number(ry); // numeric-aware: 🔥 Warmth, # Appearances sort as numbers not text
+ if(rx!==''&&ry!==''&&!isNaN(nx)&&!isNaN(ny))return (nx-ny)*dir;
return (x<y?-1:x>y?1:0)*dir}); }
return rows;
}
@@ -571,12 +578,12 @@ function renderLegend(){ let p=$('#legendpanel'); if(p&&document.activeElement&&
if(!p){p=document.createElement('div');p.id='legendpanel';p.className='legendpanel';document.body.appendChild(p);}
const items=colorTally();
const CLOSE='<button class="lgclose" title="Close (Esc)">✕</button>';
- if(!items.length){ p.innerHTML=CLOSE+'<div class="lghead">Color key — this tab</div><div class="lgempty">No color-coded cells on this tab.</div>'; p.querySelector('.lgclose').onclick=closeLegend; return; }
- p.innerHTML=CLOSE+'<div class="lghead">Color key — this tab<span class="lgnote">Exact sheet colors + provisional meaning. Click a name to rename (saved locally); ↺ resets a rename. ❓ / grey “?” = unverified guess.</span></div>'
+ if(!items.length){ p.innerHTML='<div class="lghead">'+CLOSE+'Color key — this tab</div><div class="lgempty">No color-coded cells on this tab.</div>'; p.querySelector('.lgclose').onclick=closeLegend; return; }
+ p.innerHTML='<div class="lghead">'+CLOSE+'Color key — this tab<span class="lgnote">Exact sheet colors + provisional meaning. Click a name to rename (saved locally); ↺ resets a rename. ❓ / grey “?” = unverified guess.</span></div>'
+items.map(([hx,n])=>{const sure=nameSure(hx);const ov=LS.get('cname.'+hx,null),hasOv=ov!=null&&ov!=='';const mid=hasOv?`<button class="lgreset" data-hx="${hx}" title="Reset to provisional name">↺</button>`:(sure?'':'<span class="lgq" title="unverified guess">?</span>');return `<div class="lgrow"><span class="lgsw" style="background:${hx}"></span><span class="lgname${sure?'':' lgguess'}" contenteditable="true" data-hx="${hx}" title="${sure?'Click to rename':'Heuristic guess — click to rename'}">${esc(provName(hx))}</span>${mid}<span class="lghex">${hx}</span><span class="lgn">${n.toLocaleString()}</span></div>`;}).join('');
p.querySelector('.lgclose').onclick=closeLegend;
p.querySelectorAll('.lgreset').forEach(b=>b.onclick=()=>{ localStorage.removeItem('rc2.cname.'+b.dataset.hx); renderLegend(); });
- p.querySelectorAll('.lgname').forEach(el=>{ el.onblur=()=>{ const v=el.textContent.trim(); if(v)LS.set('cname.'+el.dataset.hx, v); };
+ p.querySelectorAll('.lgname').forEach(el=>{ el.onblur=()=>{ if(window._legEsc){window._legEsc=false;return;} const v=el.textContent.trim(); if(v)LS.set('cname.'+el.dataset.hx, v); };
el.onkeydown=e=>{ if(e.key==='Enter'){e.preventDefault();el.blur();} }; });
}
function closeLegend(){ const p=$('#legendpanel'); if(p)p.style.display='none'; document.removeEventListener('mousedown',legendOutside); }
@@ -612,9 +619,15 @@ function openStatusPicker(td){
}
function closeStatusPicker(){const m=$('#statusmenu');if(m)m.remove();document.removeEventListener('mousedown',closeStatusPickerOutside);}
function closeStatusPickerOutside(e){ if(!e.target.closest('#statusmenu')&&!e.target.closest('.statuscell'))closeStatusPicker(); }
+function isWarmthCol(ci){ return DATA.headers && String(DATA.headers[ci]).trim()==='🔥 Warmth'; }
function cellTD(ri,ci,val,isN,gr){
const cls=[isN?'namecell':'',gr?'enriched':''].filter(Boolean).join(' ');
const s=String(val==null?'':val);
+ if(isWarmthCol(ci)){ // derived, read-only heat pill (hotter = deeper warm tone)
+ const n=Number(s)||0, t=Math.max(0,Math.min(1,n/120));
+ const bg=`hsl(${Math.round(16-t*6)} 90% ${Math.round(90-52*t)}%)`, fg=t>0.55?'#fff':'#5a2600';
+ return `<td data-r="${ri}" data-c="${ci}" contenteditable="false" class="warmthcell ${cls}" title="Warmth ${esc(s)} — derived (appearances + status + reachability), read-only"><span class="heatpill" style="background:${bg};color:${fg}">${esc(s)}</span></td>`;
+ }
const xc=COLORS[ri+','+ci], xt=xc?txtOn(xc):''; // EXACT original sheet cell color + auto-contrast text
const xsty=xc?` style="background:${xc};color:${xt};--xtext:${xt}"`:'', xcls=xc?' xcol':'';
if(isStatusCol(ci)){ // color-coded status chip + date/time under it + click-to-set picker
@@ -812,7 +825,7 @@ applyTheme(LS.get('theme', (window.matchMedia&&matchMedia('(prefers-color-scheme
// Fit mode = table always fits the window: re-fit columns to the viewport when it resizes
window.addEventListener('resize',()=>{ if(WIDTHPRESET==='fit'){ clearTimeout(window._rzfit); window._rzfit=setTimeout(applyFit,120); } });
document.addEventListener('dragend',()=>{ window._dragText=false; }); // clear the text-move guard after any drag
-document.addEventListener('keydown',e=>{if(e.key==='Escape'){closeDraft();closeLegend();}});
+document.addEventListener('keydown',e=>{if(e.key==='Escape'){closeDraft(); if($('#legendpanel')&&$('#legendpanel').style.display==='block'){window._legEsc=true;closeLegend();}}});
(async()=>{await loadMeta();await loadData(); if(WIDTHPRESET==='fit')applyFit();})();
</script>
</body>
← 21e6af5 rentv :9797 legend: close (✕/Esc/click-outside) + per-row ↺
·
back to Rentv Sheet Enrich Refine
·
Add derived 🔥 Warmth score column to RENTV live console + t ddffc49 →