compare: click to pin a point's highlight; hover temporarily overrides

Click a point in any panel to pin its id — highlight persists after the
cursor leaves, across all linked panels. Click the same pinned point (or
empty space) to unpin. Hover still shows the point under the cursor,
briefly overriding the pinned display. Canvas cursor is now crosshair to
hint at the interaction.
This commit is contained in:
Michael Pilosov 2026-04-22 17:00:29 -06:00
parent a4fc36352d
commit bdbebaa7e8
6 changed files with 25 additions and 9 deletions

View File

@ -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);

View File

@ -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.

View File

@ -1,7 +1,7 @@
// run-modal.js — homepage click-hijack for embedding links. Opens a
// <dialog id="run-modal"> 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');

View File

@ -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 {

View File

@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>embedding notebook &middot; compare</title>
<link rel="stylesheet" href="/static/style.css?v=31" />
<link rel="stylesheet" href="/static/style.css?v=32" />
<script type="importmap">
{
"imports": {
@ -102,7 +102,7 @@
</template>
<script src="/static/theme.js?v=11"></script>
<script type="module" src="/static/compare.js?v=16"></script>
<script type="module" src="/static/compare.js?v=17"></script>
<!-- panel-grid.js is imported by compare.js (module); versioned via compare.js cache-bust -->
</body>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>embedding notebook</title>
<link rel="stylesheet" href="/static/style.css?v=31" />
<link rel="stylesheet" href="/static/style.css?v=32" />
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script type="importmap">
{