← back to Fix Live Board
board: movable/dockable nav — drag grip (float/move up-down-anywhere) + Dock-left toggle (left sidebar panel), position+mode persisted
e6f437b0f202ab224a79f5c23f47ba5b7a3f8e16 · 2026-09-02 15:43:01 -0700 · Steve
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkjHuJKiariXaTtFujZxWC
Files touched
Diff
commit e6f437b0f202ab224a79f5c23f47ba5b7a3f8e16
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Sep 2 15:43:01 2026 -0700
board: movable/dockable nav — drag grip (float/move up-down-anywhere) + Dock-left toggle (left sidebar panel), position+mode persisted
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkjHuJKiariXaTtFujZxWC
---
public/board.html | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/public/board.html b/public/board.html
index ad7da21..6a53df3 100644
--- a/public/board.html
+++ b/public/board.html
@@ -72,6 +72,14 @@ button.tg{cursor:pointer}button.tg.on{border-color:var(--accent);color:var(--acc
.kv .k{color:var(--mut)}
.fieldrow{display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--line);font-size:12.5px}
.alink{color:var(--accent);text-decoration:none;font-size:12px}
+/* movable / dockable nav (header) */
+.grip{cursor:move;color:var(--mut);user-select:none;font-size:13px;padding:0 6px;letter-spacing:-1px}
+header.floating{position:fixed;width:auto;max-width:94vw;border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 34px #0004;z-index:30}
+header.dock-left{position:fixed;left:0;top:0;height:100vh;width:250px;overflow-y:auto;border-bottom:0;border-right:1px solid var(--line);z-index:30}
+header.dock-left .controls{flex-direction:column;align-items:flex-start;gap:10px}
+header.dock-left .controls span,header.dock-left .seg,header.dock-left select{width:100%}
+header.dock-left .meter{width:100%}
+body.nav-left .grid,body.nav-left .streams,body.nav-left .err{margin-left:266px}
</style></head><body>
<header>
<h1><span id="h1">🛠️ Fix Live Board</span></h1>
@@ -89,6 +97,8 @@ button.tg{cursor:pointer}button.tg.on{border-color:var(--accent);color:var(--acc
<span class="seg"><button id="vGrid" data-v="grid">▦ Grid</button><button id="vCols" data-v="columns">▤ Columns</button></span></span>
<span id="densWrap"><label>Density</label> <input type="range" id="dens" min="2" max="6" value="3"></span>
<button class="tg" id="theme">🌙 Night</button>
+ <button class="tg" id="dock">⇤ Dock left</button>
+ <span class="grip" id="grip" title="Drag to move (up/down / anywhere)">⠿ drag</span>
<span class="sub" id="upd"></span>
</div>
<div class="fixers" id="fixers"></div>
@@ -181,6 +191,18 @@ $('dens').addEventListener('input',e=>setCols(e.target.value));
// theme (day/night) toggle
function applyTheme(t){document.documentElement.setAttribute('data-theme',t);$('theme').textContent=t==='dark'?'☀️ Day':'🌙 Night';set('theme',t);}
$('theme').addEventListener('click',()=>applyTheme(document.documentElement.getAttribute('data-theme')==='dark'?'light':'dark'));
+// movable / dockable nav (header): drag up/down/anywhere via grip, or dock to a left panel
+const hdr=document.querySelector('header');
+function applyDock(mode){hdr.classList.remove('floating','dock-left');document.body.classList.remove('nav-left');
+ if(mode==='left'){hdr.classList.add('dock-left');document.body.classList.add('nav-left');hdr.style.left='';hdr.style.top='';}
+ else if(mode==='float'){hdr.classList.add('floating');let p={x:20,y:20};try{p=JSON.parse(get('navpos','{"x":20,"y":20}'))}catch(e){}hdr.style.left=p.x+'px';hdr.style.top=p.y+'px';}
+ else{hdr.style.left='';hdr.style.top='';}
+ set('navmode',mode);$('dock').textContent=(mode==='left')?'⇥ Dock top':'⇤ Dock left';}
+$('dock').addEventListener('click',()=>applyDock(get('navmode','top')==='left'?'top':'left'));
+(function(){let drag=false,ox=0,oy=0;const grip=$('grip');
+ grip.addEventListener('mousedown',e=>{if(get('navmode','top')!=='float')applyDock('float');drag=true;const r=hdr.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;e.preventDefault();});
+ window.addEventListener('mousemove',e=>{if(!drag)return;const x=Math.max(0,e.clientX-ox),y=Math.max(0,e.clientY-oy);hdr.style.left=x+'px';hdr.style.top=y+'px';set('navpos',JSON.stringify({x,y}));});
+ window.addEventListener('mouseup',()=>drag=false);})();
// fixers
function renderFixers(){$('fixers').innerHTML=(META.fixers||[]).map(f=>'<button class="fx '+(f.launch?'':'gated')+'" data-id="'+esc(f.id)+'" '+(f.launch?'':'disabled title="Gated — run manually"')+'>'+esc(f.label||f.id)+(f.launch?'':' · gated')+'</button>').join('');
document.querySelectorAll('.fx:not(.gated)').forEach(b=>b.addEventListener('click',async e=>{e.stopPropagation();b.disabled=true;b.textContent='running…';try{const r=await fetch('api/fix?id='+encodeURIComponent(b.dataset.id),{method:'POST'});const j=await r.json();b.textContent=(j.ok?'✓ ':'✗ ')+b.dataset.id;}catch(x){b.textContent='error';}setTimeout(poll,1200);setTimeout(renderFixers,4000);}));}
@@ -190,6 +212,7 @@ function renderFixers(){$('fixers').innerHTML=(META.fixers||[]).map(f=>'<button
const c=get('cols','3');$('dens').value=c;setCols(c);
$('sort').value=get('sort','broken');$('group').value=get('group','none');
setView(get('view','grid'));
+ applyDock(get('navmode','top'));
})();
async function loadMeta(){try{META=await(await fetch('api/meta')).json();$('h1').textContent='🛠️ '+META.name;document.title=META.name;renderFixers();}catch(e){}}
async function poll(){try{const j=await(await fetch('api/status')).json();const err=$('err');
← 7bb00cf board: default to light (black-on-white) day theme; add cred
·
back to Fix Live Board
·
Add reusable isShowroomVendor() primitive keyed off showroom 9ba8420 →