diff --git a/frontend/packages/dashboard/src/common.js b/frontend/packages/dashboard/src/common.js index 5b59da4f..8998ff46 100644 --- a/frontend/packages/dashboard/src/common.js +++ b/frontend/packages/dashboard/src/common.js @@ -216,7 +216,6 @@ export const Panel = (() => { let root = null; let titleEl = null; let bodyEl = null; - let drawer = null; /** Owner key set by `openNamed` (e.g. 'inbox'). `refresh(name, …)` * is a no-op when the current owner doesn't match, so live * updates can re-render an open view without grabbing focus @@ -230,7 +229,6 @@ export const Panel = (() => { root = $('side-panel'); titleEl = $('side-panel-title'); bodyEl = $('side-panel-body'); - drawer = root && root.querySelector('.side-panel-drawer'); } return root != null; } @@ -258,79 +256,6 @@ export const Panel = (() => { root.classList.remove('open'); root.setAttribute('aria-hidden', 'true'); } - // #451: drag-to-resize the drawer's width. Listens on a thin - // hit-strip glued to the drawer's left edge; mousedown captures - // pointermove + pointerup on the document so the drag continues - // even if the cursor strays outside the 6px handle band. Width - // persists to localStorage so it survives page reload. The CSS - // clamps the value (min-width: 320px, max-width: 96vw) — drop - // unparseable / out-of-range stored values silently. - const WIDTH_KEY = 'hyperhive:side-panel-width'; - const WIDTH_MIN = 320; - function clampWidth(w) { - const max = Math.floor(window.innerWidth * 0.96); - return Math.max(WIDTH_MIN, Math.min(max, w)); - } - function applyStoredWidth() { - if (!drawer) return; - const raw = (() => { - try { return localStorage.getItem(WIDTH_KEY); } - catch { return null; } - })(); - if (!raw) return; - const parsed = parseInt(raw, 10); - if (!Number.isFinite(parsed) || parsed <= 0) return; - drawer.style.setProperty('--side-panel-w', clampWidth(parsed) + 'px'); - } - function bindResize() { - if (!drawer) return; - const handle = document.createElement('div'); - handle.className = 'side-panel-resize'; - handle.setAttribute('role', 'separator'); - handle.setAttribute('aria-orientation', 'vertical'); - handle.setAttribute('aria-label', 'drag to resize side panel'); - handle.title = 'drag to resize'; - drawer.prepend(handle); - let dragging = false; - handle.addEventListener('pointerdown', (e) => { - e.preventDefault(); - dragging = true; - document.body.classList.add('side-panel-resizing'); - // Capture so we keep getting pointermove even when the cursor - // outpaces the handle band (drag-fast-then-pause loses the - // handle's :hover state otherwise). - try { handle.setPointerCapture(e.pointerId); } catch { /* legacy */ } - }); - document.addEventListener('pointermove', (e) => { - if (!dragging) return; - // Drawer is anchored to the right edge — width = viewport - pointer X. - const w = clampWidth(window.innerWidth - e.clientX); - drawer.style.setProperty('--side-panel-w', w + 'px'); - }); - function stopDrag() { - if (!dragging) return; - dragging = false; - document.body.classList.remove('side-panel-resizing'); - // Persist the final width. Read the actual rendered width - // rather than re-deriving so the stored value matches what - // the operator saw at mouseup. - const w = drawer.getBoundingClientRect().width; - try { localStorage.setItem(WIDTH_KEY, String(Math.round(w))); } - catch { /* localStorage unavailable — width is session-only */ } - } - document.addEventListener('pointerup', stopDrag); - document.addEventListener('pointercancel', stopDrag); - // Re-clamp on viewport resize so a persisted width that exceeds - // 96vw doesn't push the drawer off-screen after a window shrink. - window.addEventListener('resize', () => { - if (dragging) return; - const cur = drawer.getBoundingClientRect().width; - const clamped = clampWidth(cur); - if (clamped !== Math.round(cur)) { - drawer.style.setProperty('--side-panel-w', clamped + 'px'); - } - }); - } function bind() { if (!ensure()) return; $('side-panel-close').addEventListener('click', close); @@ -338,8 +263,6 @@ export const Panel = (() => { document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && root.classList.contains('open')) close(); }); - applyStoredWidth(); - bindResize(); } return { open, openNamed, refresh, close, bind }; })(); diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index b76e9f53..3caffb1c 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -1111,10 +1111,8 @@ summary:hover { color: var(--purple); } background: var(--bg-elev); border: 1px solid var(--border); padding: 0.5em 0.8em; - /* #450: no max-height cap — let the inbox grow to fill the - side-panel-body which already scrolls (`overflow: auto`). The - pre-#450 24em cap clamped the list well short of the available - panel height even on tall viewports. */ + max-height: 24em; + overflow-y: auto; } .inbox li { padding: 0.25em 0; @@ -1299,19 +1297,7 @@ footer .banner-thin { top: 0; right: 0; bottom: 0; - /* #451: width is a CSS variable so the drag handle (added by - Panel.bind) can update it live, and so localStorage-persisted - widths apply on first paint. Default min(760px, 94vw) preserves - the pre-#451 behaviour for operators who never drag. JS sets - `--side-panel-w` via inline style on the drawer; persistence - lives in localStorage (key `hyperhive:side-panel-width`), - replayed onto the var by `Panel.applyStoredWidth` at bind. */ - width: var(--side-panel-w, min(760px, 94vw)); - /* Clamp so a stored width can never push the drawer off-screen - or shrink it past readability. min content width matches the - panel-head's natural width with the close button visible. */ - min-width: 320px; - max-width: 96vw; + width: min(760px, 94vw); display: flex; flex-direction: column; background: var(--bg-elev); @@ -1323,36 +1309,6 @@ footer .banner-thin { .side-panel.open { pointer-events: auto; } .side-panel.open .side-panel-backdrop { opacity: 1; } .side-panel.open .side-panel-drawer { transform: translateX(0); } -/* #451: drag-to-resize handle on the drawer's left edge. The handle - itself is invisible until hover/drag so it doesn't compete with the - 2px mauve `border-left` for the visual boundary. Pointer-cursor - tells the operator the edge is grabbable; the brighter glow during - drag (`body.side-panel-resizing`) is the affordance the eye - tracks. */ -.side-panel-resize { - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: 6px; - margin-left: -3px; - cursor: ew-resize; - z-index: 1; - background: transparent; - transition: background 120ms ease; -} -.side-panel-resize:hover, -body.side-panel-resizing .side-panel-resize { - background: rgba(203, 166, 247, 0.55); -} -/* While dragging: kill text selection + force the resize cursor - page-wide so the cursor doesn't flip back to default the moment it - leaves the 6px handle band. */ -body.side-panel-resizing { - cursor: ew-resize !important; - user-select: none; -} -body.side-panel-resizing * { cursor: ew-resize !important; } .side-panel-head { flex: 0 0 auto; display: flex;