From 5ce9ee999c210a4be92ef4266c7bc6879741fdd3 Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 6 Oct 2026 13:05:33 -0400 Subject: [PATCH 1/2] Card Builder: drag grips to resize a fixed width or height Co-Authored-By: Claude Sonnet 5.5 --- src/lib/cardbuilder/builder.js | 2 +- src/lib/cardbuilder/overlay.js | 34 +++++++++++++++++++++++++++++ src/lib/cardbuilder/preview.js | 5 +++-- src/styles/card-builder-preview.css | 6 +++++ 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/lib/cardbuilder/builder.js b/src/lib/cardbuilder/builder.js index faf0a04d..6429f986 100644 --- a/src/lib/cardbuilder/builder.js +++ b/src/lib/cardbuilder/builder.js @@ -582,7 +582,7 @@ export function mountCardBuilder({ form, data }) { } // The surface the preview's modules are given. - const b = { form, el, items, space, areasOn, st, trackBetween, trackName, showTracks, tune, render, setRadio, sideKind, showGap, stackedIn, moveButton }; + const b = { limits, form, el, items, space, areasOn, st, trackBetween, trackName, showTracks, tune, render, setRadio, sideKind, showGap, stackedIn, moveButton }; tuneAll.addEventListener('click', () => tune(tuneAll.textContent === 'hide all' ? [] : tuneRows.map((r) => r.dataset.tune))); // The PNG or PDF, drawn here from the preview when asked for. diff --git a/src/lib/cardbuilder/overlay.js b/src/lib/cardbuilder/overlay.js index bf5cf8dd..9cd725f7 100644 --- a/src/lib/cardbuilder/overlay.js +++ b/src/lib/cardbuilder/overlay.js @@ -56,6 +56,40 @@ export function attachGrip(frame, b) { }); } +/** A card of fixed width or height has a grip on its right or bottom edge: + * dragging it sizes that dimension live, and on release writes the px box. */ +export function attachSize(frame, b) { + const { form, render, limits } = b; + const doc = frame.contentDocument, card = doc.querySelector('.card'); + if (!card) return; + const { fixedMin, fixedMax, fixedMinHeight } = limits; + for (const [axis, mode, box, lo] of [['w', 'wmode', 'px', fixedMin], ['h', 'hmode', 'hpx', fixedMinHeight]]) { + if (form.elements[mode].value !== 'px') continue; + const g = doc.createElement('span'); + g.className = 'eb-size-grip'; + g.dataset.axis = axis; + g.title = `Drag to set the card’s ${axis === 'w' ? 'width' : 'height'}`; + card.append(g); + g.addEventListener('pointerdown', (e) => { + e.preventDefault(); + e.stopPropagation(); + g.setPointerCapture(e.pointerId); + const r = card.getBoundingClientRect(), x0 = e.clientX, y0 = e.clientY; + let v = null; + const move = (ev) => { + v = clampInt(axis === 'w' ? r.width + ev.clientX - x0 : r.height + ev.clientY - y0, lo, fixedMax); + if (axis === 'w') frame.style.width = `${v}px`; else card.style.height = `${v}px`; + }; + const up = () => { + if (v == null) return; + form.elements[box].value = v; + render(); + }; + drag(g, move, up); + }); + } +} + /** In the preview, a ⊕ between each two slots that show — the first track * between them, where parts left out leave several — to add space there, * and a hatched band over each track that has some. */ diff --git a/src/lib/cardbuilder/preview.js b/src/lib/cardbuilder/preview.js index 0ad32fca..58c0c1b3 100644 --- a/src/lib/cardbuilder/preview.js +++ b/src/lib/cardbuilder/preview.js @@ -12,7 +12,7 @@ import { FIGURE_SLOTS } from '../cardname.js'; import { inBox } from './model.js'; import { cellGrid, partZones, venueDrops } from './geometry.js'; import { drag, shownIn } from './dom.js'; -import { attachGrip, attachPlus } from './overlay.js'; +import { attachGrip, attachPlus, attachSize } from './overlay.js'; // The preview's hitboxes: each part of the card, outlined, brings up what // tunes it — first match wins, so a link button beats the box it sits in, @@ -58,6 +58,7 @@ export function attachPreview(frame, b) { doc.documentElement.classList.add('eb-hits'); attachGrip(frame, b); attachPlus(frame, b); + attachSize(frame, b); // A part is what it is; what is left — the card's own room, the box a // buttons' box fills its side with, or a title's box past its words — is the // area under the pointer, where there is one, not the part that happens @@ -72,7 +73,7 @@ export function attachPreview(frame, b) { return [...r.getClientRects()].some((b) => x >= b.left - 2 && x <= b.right + 2 && y >= b.top - 2 && y <= b.bottom + 2); }; const find = (t, x, y) => { - if (t.closest?.('.eb-plus, .eb-band, .eb-area-grip, .eb-areabtn')) return null; + if (t.closest?.('.eb-plus, .eb-band, .eb-area-grip, .eb-size-grip, .eb-areabtn')) return null; for (const [q, keys] of HITS) { const el = t.closest?.(q); if (!el) continue; diff --git a/src/styles/card-builder-preview.css b/src/styles/card-builder-preview.css index 1f5a38db..b13f0650 100644 --- a/src/styles/card-builder-preview.css +++ b/src/styles/card-builder-preview.css @@ -28,6 +28,12 @@ border:2px solid var(--accent); border-radius:2px; background:var(--ground); cursor:nwse-resize; touch-action:none} .eb-hits .card{position:relative; isolation:isolate} /* The areas, behind what is in them: outlined, as the parts are. */ +.eb-size-grip{display:none} +.eb-hits .eb-size-grip{display:block; position:absolute; z-index:7; touch-action:none; background:var(--accent); opacity:0; transition:opacity .15s} +.eb-hits .card:hover .eb-size-grip{opacity:.35} +.eb-hits .eb-size-grip:hover, .eb-hits .eb-size-grip:active{opacity:1} +.eb-hits .eb-size-grip[data-axis=w]{top:0; bottom:0; right:0; width:6px; cursor:ew-resize} +.eb-hits .eb-size-grip[data-axis=h]{left:0; right:0; bottom:0; height:6px; cursor:ns-resize} .eb-area{display:none} .eb-hits .eb-area{display:block; position:absolute; z-index:-1} /* A side's inner edge, dragged to set its width. */ From abc85d079b713b6a9130701a9f43978139008b8e Mon Sep 17 00:00:00 2001 From: nstarman Date: Tue, 6 Oct 2026 13:21:30 -0400 Subject: [PATCH 2/2] Card Builder: hide the preset widths Co-Authored-By: Claude Sonnet 5.5 --- src/lib/cardbuilder/builder.js | 2 +- src/pages/tools/card.astro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/cardbuilder/builder.js b/src/lib/cardbuilder/builder.js index 6429f986..e6ed6f16 100644 --- a/src/lib/cardbuilder/builder.js +++ b/src/lib/cardbuilder/builder.js @@ -240,7 +240,7 @@ export function mountCardBuilder({ form, data }) { wasSet.wmode = wset; wasSet.hmode = hset; el('eb-px').hidden = !widthOpen; - el('eb-px-w').hidden = el('eb-widths').hidden = !wset; + el('eb-px-w').hidden = !wset; el('eb-px-h').hidden = !hset; el('eb-shapes').hidden = !(wset && hset); el('eb-w-shown').textContent = wset && !widthOpen ? `${s.width}px` : 'set'; diff --git a/src/pages/tools/card.astro b/src/pages/tools/card.astro index fe45ea95..41971532 100644 --- a/src/pages/tools/card.astro +++ b/src/pages/tools/card.astro @@ -102,7 +102,7 @@ const data = {