compare: axes dropdown combines sync (scaled/locked) × aspect (1:1/3:2)

Canvas height now derives from column width via aspect-ratio (CSS custom
prop --canvas-aspect set by JS on the grid host), with --panel-h as a
ceiling. Dropdown options: scaled/locked × 1:1/3:2. Default scaled 3:2.
Legacy 'independent'/'locked' values still parse. Canvas resizes after
aspect changes via requestAnimationFrame.
This commit is contained in:
Michael Pilosov 2026-04-22 16:57:15 -06:00
parent 9b178dad38
commit a4fc36352d
6 changed files with 40 additions and 17 deletions

View File

@ -1,7 +1,7 @@
// compare.js — thin shim that parses ?stem=…&stem=… (legacy ?a=&b=) and // compare.js — thin shim that parses ?stem=…&stem=… (legacy ?a=&b=) and
// hands off to panel-grid.js. // hands off to panel-grid.js.
import { mountPanels } from './panel-grid.js?v=1'; import { mountPanels } from './panel-grid.js?v=4';
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
let stems = params.getAll('stem').filter(Boolean); let stems = params.getAll('stem').filter(Boolean);

View File

@ -628,9 +628,21 @@ export function mountPanels({ host, controls, stems }) {
} }
} }
function applySync() { function parseAxes(value) {
const mode = syncSel ? syncSel.value : 'independent'; // Accepted: "scaled-1x1", "scaled-3x2", "locked-1x1", "locked-3x2",
if (mode === 'locked' && live.length > 1) { // plus legacy "independent" / "locked" (assume 1:1).
const v = value || 'scaled-1x1';
if (v === 'independent') return { sync: 'scaled', aspect: '1 / 1' };
if (v === 'locked') return { sync: 'locked', aspect: '1 / 1' };
const [sync, ratio] = v.split('-');
const aspect = ratio === '3x2' ? '3 / 2' : '1 / 1';
return { sync: sync === 'locked' ? 'locked' : 'scaled', aspect };
}
function applyAxes() {
const { sync, aspect } = parseAxes(syncSel ? syncSel.value : 'scaled-3x2');
host.style.setProperty('--canvas-aspect', aspect);
if (sync === 'locked' && live.length > 1) {
let xmin = +Infinity, xmax = -Infinity, ymin = +Infinity, ymax = -Infinity; let xmin = +Infinity, xmax = -Infinity, ymin = +Infinity, ymax = -Infinity;
for (const s of live) { for (const s of live) {
const b = s.panel.data.bounds; const b = s.panel.data.bounds;
@ -644,9 +656,11 @@ export function mountPanels({ host, controls, stems }) {
} else { } else {
for (const s of live) s.panel.setBounds(s.panel.data.bounds); for (const s of live) s.panel.setBounds(s.panel.data.bounds);
} }
// Canvas size changes with aspect — let the panels re-fit next frame.
requestAnimationFrame(() => { for (const s of live) s.panel.resize(); });
} }
if (syncSel) onCtl(syncSel, 'change', applySync); if (syncSel) onCtl(syncSel, 'change', applyAxes);
applySync(); applyAxes();
let playing = false; let playing = false;
let lastTs = 0; let lastTs = 0;

View File

@ -1,7 +1,7 @@
// run-modal.js — homepage click-hijack for embedding links. Opens a // 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. // <dialog id="run-modal"> that renders the run's embedding via panel-grid.js.
import { mountPanels } from './panel-grid.js?v=1'; import { mountPanels } from './panel-grid.js?v=4';
const dialog = document.getElementById('run-modal'); const dialog = document.getElementById('run-modal');
const host = document.getElementById('modal-panel-host'); const host = document.getElementById('modal-panel-host');

View File

@ -1587,10 +1587,13 @@ button.submit:disabled { background: var(--faint); border-color: var(--faint); c
.compare-canvas { .compare-canvas {
position: relative; position: relative;
flex: 1 1 auto; flex: 0 0 auto;
min-height: 0; min-height: 0;
background: var(--picker-panel, var(--panel)); background: var(--picker-panel, var(--panel));
height: var(--panel-h); /* Height derives from column width × aspect ratio. Controlled by the
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);
} }
.compare-canvas canvas { .compare-canvas canvas {

View File

@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>embedding notebook &middot; compare</title> <title>embedding notebook &middot; compare</title>
<link rel="stylesheet" href="/static/style.css?v=29" /> <link rel="stylesheet" href="/static/style.css?v=31" />
<script type="importmap"> <script type="importmap">
{ {
"imports": { "imports": {
@ -73,8 +73,10 @@
<label class="cc-sync-wrap"> <label class="cc-sync-wrap">
<span class="cc-lbl">axes</span> <span class="cc-lbl">axes</span>
<select class="cc-sync" id="cc-sync"> <select class="cc-sync" id="cc-sync">
<option value="independent" selected>independent</option> <option value="scaled-1x1">scaled &middot; 1:1</option>
<option value="locked">locked</option> <option value="scaled-3x2" selected>scaled &middot; 3:2</option>
<option value="locked-1x1">locked &middot; 1:1</option>
<option value="locked-3x2">locked &middot; 3:2</option>
</select> </select>
</label> </label>
</div> </div>
@ -100,6 +102,8 @@
</template> </template>
<script src="/static/theme.js?v=11"></script> <script src="/static/theme.js?v=11"></script>
<script type="module" src="/static/compare.js?v=11"></script> <script type="module" src="/static/compare.js?v=16"></script>
<!-- panel-grid.js is imported by compare.js (module); versioned via compare.js cache-bust -->
</body> </body>
</html> </html>

View File

@ -4,7 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>embedding notebook</title> <title>embedding notebook</title>
<link rel="stylesheet" href="/static/style.css?v=29" /> <link rel="stylesheet" href="/static/style.css?v=31" />
<script src="https://unpkg.com/htmx.org@2.0.4"></script> <script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script type="importmap"> <script type="importmap">
{ {
@ -445,8 +445,10 @@
<label class="cc-sync-wrap"> <label class="cc-sync-wrap">
<span class="cc-lbl">axes</span> <span class="cc-lbl">axes</span>
<select class="cc-sync" id="cc-sync"> <select class="cc-sync" id="cc-sync">
<option value="independent" selected>independent</option> <option value="scaled-1x1">scaled &middot; 1:1</option>
<option value="locked">locked</option> <option value="scaled-3x2" selected>scaled &middot; 3:2</option>
<option value="locked-1x1">locked &middot; 1:1</option>
<option value="locked-3x2">locked &middot; 3:2</option>
</select> </select>
</label> </label>
</div> </div>
@ -484,7 +486,7 @@
<script type="module" src="/static/metrics.js?v=11"></script> <script type="module" src="/static/metrics.js?v=11"></script>
<script src="/static/compare-select.js?v=2"></script> <script src="/static/compare-select.js?v=2"></script>
<script src="/static/runs-filter.js?v=1"></script> <script src="/static/runs-filter.js?v=1"></script>
<script type="module" src="/static/run-modal.js?v=1"></script> <script type="module" src="/static/run-modal.js?v=2"></script>
<script> <script>
// Anchor-links alone don't expand <details>; force it. // Anchor-links alone don't expand <details>; force it.
document.querySelector('a[href="#metrics"]')?.addEventListener('click', () => { document.querySelector('a[href="#metrics"]')?.addEventListener('click', () => {