diff --git a/app/web/static/compare.js b/app/web/static/compare.js index 9fefffd..6b808bd 100644 --- a/app/web/static/compare.js +++ b/app/web/static/compare.js @@ -1,7 +1,7 @@ // compare.js — thin shim that parses ?stem=…&stem=… (legacy ?a=&b=) and // hands off to panel-grid.js. -import { mountPanels } from './panel-grid.js?v=4'; +import { mountPanels } from './panel-grid.js?v=5'; const params = new URLSearchParams(window.location.search); let stems = params.getAll('stem').filter(Boolean); diff --git a/app/web/static/panel-grid.js b/app/web/static/panel-grid.js index 194819f..ee67a19 100644 --- a/app/web/static/panel-grid.js +++ b/app/web/static/panel-grid.js @@ -713,18 +713,33 @@ export function mountPanels({ host, controls, stems }) { if (colorSel) onCtl(colorSel, 'change', applyColorMode); applyColorMode(); - // Linked hover across every panel. + // Linked hover + click-to-pin. Hover shows the point under the cursor + // across all panels. Click toggles a "pinned" id that stays highlighted + // once the cursor leaves; hover temporarily overrides the display. + let pinnedId = null; + let hoveredId = null; + function paintHighlight() { + const id = hoveredId !== null ? hoveredId : pinnedId; + for (const t of live) t.panel.setHighlight(id); + } for (const s of live) { const mm = (ev) => { const id = s.panel.pickAt(ev.clientX, ev.clientY); - const hid = id >= 0 ? id : null; - for (const t of live) t.panel.setHighlight(hid); + hoveredId = id >= 0 ? id : null; + paintHighlight(); + }; + const ml = () => { hoveredId = null; paintHighlight(); }; + const mc = (ev) => { + const id = s.panel.pickAt(ev.clientX, ev.clientY); + pinnedId = (id < 0 || id === pinnedId) ? null : id; + paintHighlight(); }; - const ml = () => { for (const t of live) t.panel.setHighlight(null); }; s.panel.canvasEl.addEventListener('mousemove', mm); s.panel.canvasEl.addEventListener('mouseleave', ml); + s.panel.canvasEl.addEventListener('click', mc); listeners.push({ el: s.panel.canvasEl, ev: 'mousemove', fn: mm }); listeners.push({ el: s.panel.canvasEl, ev: 'mouseleave', fn: ml }); + listeners.push({ el: s.panel.canvasEl, ev: 'click', fn: mc }); } // Resize + theme observers. diff --git a/app/web/static/run-modal.js b/app/web/static/run-modal.js index c996d0a..29b0b39 100644 --- a/app/web/static/run-modal.js +++ b/app/web/static/run-modal.js @@ -1,7 +1,7 @@ // run-modal.js — homepage click-hijack for embedding links. Opens a // that renders the run's embedding via panel-grid.js. -import { mountPanels } from './panel-grid.js?v=4'; +import { mountPanels } from './panel-grid.js?v=5'; const dialog = document.getElementById('run-modal'); const host = document.getElementById('modal-panel-host'); diff --git a/app/web/static/style.css b/app/web/static/style.css index f23461d..b070979 100644 --- a/app/web/static/style.css +++ b/app/web/static/style.css @@ -1594,6 +1594,7 @@ button.submit:disabled { background: var(--faint); border-color: var(--faint); c axes dropdown via --canvas-aspect on the grid. --panel-h is the cap. */ aspect-ratio: var(--canvas-aspect, 1 / 1); max-height: var(--panel-h); + cursor: crosshair; } .compare-canvas canvas { diff --git a/app/web/templates/compare.html b/app/web/templates/compare.html index f9ec60b..8746396 100644 --- a/app/web/templates/compare.html +++ b/app/web/templates/compare.html @@ -4,7 +4,7 @@ embedding notebook · compare - + - + diff --git a/app/web/templates/index.html b/app/web/templates/index.html index 7bf6295..0fc1169 100644 --- a/app/web/templates/index.html +++ b/app/web/templates/index.html @@ -4,7 +4,7 @@ embedding notebook - +