Increase font size ranges; add save/load design as JSON

This commit is contained in:
2026-04-25 23:04:13 -04:00
parent 6ea661f439
commit 60efc1c12e
+88 -3
View File
@@ -291,7 +291,7 @@
</div>
<div class="row">
<label>Number font size</label>
<input type="range" id="numFontSize" min="1" max="8" value="3" step="0.5">
<input type="range" id="numFontSize" min="1" max="25" value="3" step="0.5">
<span class="val" id="numFontSizeVal">3%</span>
</div>
</div>
@@ -363,7 +363,7 @@
</div>
<div class="row">
<label>Title size %</label>
<input type="range" id="titleSize" min="2" max="15" value="6">
<input type="range" id="titleSize" min="2" max="30" value="6">
<span class="val" id="titleSizeVal">6%</span>
</div>
<div class="row">
@@ -377,7 +377,7 @@
</div>
<div class="row">
<label>Subtitle size %</label>
<input type="range" id="subtitleSize" min="1" max="10" value="3">
<input type="range" id="subtitleSize" min="1" max="25" value="3">
<span class="val" id="subtitleSizeVal">3%</span>
</div>
<div class="row">
@@ -484,6 +484,10 @@
<div class="hint" style="margin-top:6px;text-align:center">
PDF prints at exact physical size.<br>Print at 100% — no scaling.
</div>
<hr class="divider" style="margin-top:10px">
<button id="saveDesignBtn" class="secondary">Save Design (.json)</button>
<button id="loadDesignBtn" class="secondary">Load Design (.json)</button>
<input type="file" id="loadDesignInput" accept=".json" style="display:none">
</div>
</div>
@@ -1018,6 +1022,87 @@ $('exportPngBtn').addEventListener('click', () => {
link.click();
});
// ── save / load design ────────────────────────────────────────────────────────
$('saveDesignBtn').addEventListener('click', () => {
const data = { version: 1, gaugeMode, sweepDir, zones: JSON.parse(JSON.stringify(zones)) };
document.querySelectorAll('#sidebar input, #sidebar select').forEach(el => {
if (!el.id || el.type === 'file') return;
data[el.id] = el.type === 'checkbox' ? el.checked : el.value;
});
if (logoImg) {
data.logoDataUrl = logoImg.src;
data.logoFileName = $('logoFileName').textContent;
}
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `${$('gaugeTitle').value || 'gauge'}-design.json`;
a.click();
URL.revokeObjectURL(a.href);
});
$('loadDesignBtn').addEventListener('click', () => $('loadDesignInput').click());
$('loadDesignInput').addEventListener('change', e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = ev => {
let data;
try { data = JSON.parse(ev.target.result); } catch { alert('Invalid design file.'); return; }
// restore all inputs silently (no events yet)
document.querySelectorAll('#sidebar input, #sidebar select').forEach(el => {
if (!el.id || el.type === 'file' || !(el.id in data)) return;
if (el.type === 'checkbox') el.checked = data[el.id];
else el.value = data[el.id];
});
// update range display spans by firing input events on ranges
document.querySelectorAll('#sidebar input[type=range]').forEach(el => {
el.dispatchEvent(new Event('input'));
});
// restore gauge mode UI
gaugeMode = data.gaugeMode || 'standard';
if (gaugeMode === 'center') {
$('modeCtr').classList.add('active'); $('modeStd').classList.remove('active');
$('ctrFields').style.display = ''; $('stdFields').style.display = 'none';
$('ctrTickNote').style.display = ''; $('stdTickNote').style.display = 'none';
$('stdMajorInterval').style.display = 'none';
} else {
$('modeStd').classList.add('active'); $('modeCtr').classList.remove('active');
$('stdFields').style.display = ''; $('ctrFields').style.display = 'none';
$('stdTickNote').style.display = ''; $('ctrTickNote').style.display = 'none';
$('stdMajorInterval').style.display = '';
}
// restore sweep direction UI
sweepDir = data.sweepDir || 'cw';
if (sweepDir === 'ccw') {
$('dirCCW').classList.add('active'); $('dirCW').classList.remove('active');
} else {
$('dirCW').classList.add('active'); $('dirCCW').classList.remove('active');
}
// restore zones
zones = data.zones || [];
renderZones();
// restore logo
if (data.logoDataUrl) {
const img = new Image();
img.onload = () => { setLogo(img, data.logoFileName || 'logo'); drawGauge(); };
img.src = data.logoDataUrl;
} else {
clearLogo();
drawGauge();
}
};
reader.readAsText(file);
e.target.value = '';
});
// ── preview background toggle ─────────────────────────────────────────────────
const previewArea = $('preview-area');