diff --git a/web/app.js b/web/app.js index 5bff2e8..a34351d 100644 --- a/web/app.js +++ b/web/app.js @@ -1451,6 +1451,14 @@ return; } + if (ev.key === 'Escape' && $('main').classList.contains('focus-peek')) { + exitFocusPeek(); + state.selectedIndex = -1; + renderEntityList(); + renderDetailPane(); + return; + } + const sel = state.entities[state.selectedIndex]; switch (ev.key) { @@ -1534,6 +1542,17 @@ function toggleZen() { const m = $('main'); + + if (m.classList.contains('focus-peek')) { + exitFocusPeek(); + return; + } + + if (state.selectedIndex >= 0) { + m.classList.add('focus-peek'); + return; + } + const isZen = m.classList.contains('hide-rail') && m.classList.contains('hide-peek'); if (isZen) { m.classList.remove('hide-rail', 'hide-peek'); @@ -1546,12 +1565,72 @@ } } + function exitFocusPeek() { + $('main').classList.remove('focus-peek'); + } + (function restorePanels() { const m = $('main'); if (localStorage.getItem('nib:hide-rail')) m.classList.add('hide-rail'); if (localStorage.getItem('nib:hide-peek')) m.classList.add('hide-peek'); + const railW = localStorage.getItem('nib:rail-w'); + const peekW = localStorage.getItem('nib:peek-w'); + if (railW) m.style.setProperty('--rail-w', railW + 'px'); + if (peekW) m.style.setProperty('--peek-w', peekW + 'px'); })(); + // ========== Resize handles ========== + + $$('.resize-handle').forEach(handle => { + let startX, startW, panel; + + handle.addEventListener('mousedown', (ev) => { + ev.preventDefault(); + panel = handle.dataset.panel; + startX = ev.clientX; + const m = $('main'); + m.classList.add('resizing'); + handle.classList.add('active'); + + if (panel === 'rail') { + startW = $('#tag-rail').offsetWidth; + } else { + startW = $('#detail-pane').offsetWidth; + } + + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + }); + + function onMove(ev) { + const m = $('main'); + const dx = ev.clientX - startX; + let newW; + + if (panel === 'rail') { + newW = Math.max(120, Math.min(360, startW + dx)); + m.style.setProperty('--rail-w', newW + 'px'); + } else { + newW = Math.max(250, Math.min(700, startW - dx)); + m.style.setProperty('--peek-w', newW + 'px'); + } + } + + function onUp() { + const m = $('main'); + m.classList.remove('resizing'); + handle.classList.remove('active'); + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + + if (panel === 'rail') { + localStorage.setItem('nib:rail-w', $('#tag-rail').offsetWidth); + } else { + localStorage.setItem('nib:peek-w', $('#detail-pane').offsetWidth); + } + } + }); + function scrollSelectedIntoView() { const el = $(`.entity-item[data-index="${state.selectedIndex}"], .card-row[data-index="${state.selectedIndex}"]`); if (el) el.scrollIntoView({ block: 'nearest' }); diff --git a/web/index.html b/web/index.html index cc63663..60990d2 100644 --- a/web/index.html +++ b/web/index.html @@ -24,11 +24,13 @@
+
+
diff --git a/web/style.css b/web/style.css index eb6f779..2e3863a 100644 --- a/web/style.css +++ b/web/style.css @@ -177,17 +177,39 @@ nav { display: flex; gap: 2px; } /* ── MAIN LAYOUT ────────────────────────────────────── */ main { display: grid; - grid-template-columns: 192px 1fr 400px; + grid-template-columns: var(--rail-w, 192px) 4px 1fr 4px var(--peek-w, 400px); overflow: hidden; - transition: grid-template-columns var(--t-base); } -main.hide-rail { grid-template-columns: 0px 1fr 400px; } -main.hide-peek { grid-template-columns: 192px 1fr 0px; } -main.hide-rail.hide-peek { grid-template-columns: 0px 1fr 0px; } +main.resizing { transition: none; } +main:not(.resizing) { transition: grid-template-columns var(--t-base); } -main.hide-rail #tag-rail { overflow: hidden; border-right: none; } -main.hide-peek #detail-pane { overflow: hidden; border-left: none; } +main.hide-rail { grid-template-columns: 0px 0px 1fr 4px var(--peek-w, 400px); } +main.hide-peek { grid-template-columns: var(--rail-w, 192px) 4px 1fr 0px 0px; } +main.hide-rail.hide-peek { grid-template-columns: 0px 0px 1fr 0px 0px; } + +main.hide-rail #tag-rail { overflow: hidden; border-right: none; min-width: 0; } +main.hide-peek #detail-pane { overflow: hidden; border-left: none; min-width: 0; } +main.hide-rail .resize-handle[data-panel="rail"] { visibility: hidden; } +main.hide-peek .resize-handle[data-panel="peek"] { visibility: hidden; } + +main.focus-peek { grid-template-columns: 0px 0px 0px 0px 1fr; } +main.focus-peek #tag-rail { overflow: hidden; border-right: none; min-width: 0; } +main.focus-peek #entity-panel { overflow: hidden; min-width: 0; } +main.focus-peek .resize-handle { visibility: hidden; } + +.resize-handle { + cursor: col-resize; + background: transparent; + transition: background var(--t-fast); + z-index: 10; +} + +.resize-handle:hover, +.resize-handle.active { + background: var(--accent); + opacity: .4; +} /* ── TAG RAIL ───────────────────────────────────────── */ #tag-rail {