feat(ui): resizable rail and peek pane

- Drag handles between rail/center and center/peek
- Rail: 120–360px range, peek: 250–700px range
- Widths persisted in localStorage
- Handles hidden when panel is collapsed (zen mode)
- Transition disabled during drag for smooth resize
This commit is contained in:
2026-05-16 20:42:20 -04:00
parent a399c4fb15
commit 0316076bf8
3 changed files with 82 additions and 7 deletions
+56
View File
@@ -1550,8 +1550,64 @@
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' });