Palette calibration: precise hex/RGB inputs instead of a color picker
Build and push server image / build-and-push (push) Successful in 41s
Build and push server image / build-and-push (push) Successful in 41s
A native <input type="color"> swatch can't be typed into precisely -- no way to enter an exact measured value. Replaced with a small table: a read-only preview swatch, a hex text field, and three 0-255 number fields (R/G/B) per ink color, kept in sync live in both directions (editing hex updates R/G/B and the swatch; editing any of R/G/B updates hex and the swatch). Hex stays the field actually read at save time -- the server-side validation (#rrggbb via hex_to_rgb) is unchanged, this is a client-side-only swap of the input widget.
This commit is contained in:
@@ -67,10 +67,56 @@ async function loadControl() {
|
||||
document.getElementById('take-control').addEventListener('click', takeControl);
|
||||
|
||||
// ---- Advanced configuration: color palette ----
|
||||
//
|
||||
// Hex field and R/G/B number fields are kept in sync live, both
|
||||
// directions -- editing either updates the other plus the preview
|
||||
// swatch. Hex stays the field actually read at save time (it's what
|
||||
// the server already validates as #rrggbb); the R/G/B fields are purely
|
||||
// an alternate, more precise way to arrive at the same value than
|
||||
// eyeballing a color-picker swatch.
|
||||
|
||||
function paletteInputs() {
|
||||
return Array.from(document.querySelectorAll('[id^="palette_"]'))
|
||||
.sort((a, b) => Number(a.id.split('_')[1]) - Number(b.id.split('_')[1]));
|
||||
function paletteHexInputs() {
|
||||
return Array.from(document.querySelectorAll('.palette-hex'))
|
||||
.sort((a, b) => Number(a.dataset.index) - Number(b.dataset.index));
|
||||
}
|
||||
|
||||
function hexFromRgb(r, g, b) {
|
||||
const clamp = (v) => Math.max(0, Math.min(255, Math.round(Number(v) || 0)));
|
||||
return '#' + [r, g, b].map((v) => clamp(v).toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
function rgbFromHex(hex) {
|
||||
const m = /^#?([0-9a-f]{6})$/i.exec((hex || '').trim());
|
||||
if (!m) return null;
|
||||
const n = parseInt(m[1], 16);
|
||||
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
||||
}
|
||||
|
||||
function paletteFieldsFor(index) {
|
||||
const at = (cls) => document.querySelector(`.${cls}[data-index="${index}"]`);
|
||||
return { hex: at('palette-hex'), r: at('palette-r'), g: at('palette-g'), b: at('palette-b'), swatch: at('palette-swatch-preview') };
|
||||
}
|
||||
|
||||
function syncPaletteFromHex(index) {
|
||||
const f = paletteFieldsFor(index);
|
||||
const rgb = rgbFromHex(f.hex.value);
|
||||
if (!rgb) return;
|
||||
[f.r.value, f.g.value, f.b.value] = rgb;
|
||||
f.swatch.style.background = f.hex.value;
|
||||
}
|
||||
|
||||
function syncPaletteFromRgb(index) {
|
||||
const f = paletteFieldsFor(index);
|
||||
const hex = hexFromRgb(f.r.value, f.g.value, f.b.value);
|
||||
f.hex.value = hex;
|
||||
f.swatch.style.background = hex;
|
||||
}
|
||||
|
||||
const palettePickerCount = paletteHexInputs().length;
|
||||
for (let i = 0; i < palettePickerCount; i++) {
|
||||
const f = paletteFieldsFor(i);
|
||||
f.hex.addEventListener('input', () => syncPaletteFromHex(i));
|
||||
[f.r, f.g, f.b].forEach((el) => el.addEventListener('input', () => syncPaletteFromRgb(i)));
|
||||
}
|
||||
|
||||
async function savePalette(body) {
|
||||
@@ -89,15 +135,18 @@ async function savePalette(body) {
|
||||
|
||||
document.getElementById('palette-save').addEventListener('click', () => {
|
||||
const body = new URLSearchParams();
|
||||
for (const input of paletteInputs()) {
|
||||
for (const input of paletteHexInputs()) {
|
||||
body.append('palette', input.value);
|
||||
}
|
||||
savePalette(body);
|
||||
});
|
||||
|
||||
document.getElementById('palette-reset').addEventListener('click', () => {
|
||||
const inputs = paletteInputs();
|
||||
window.DEFAULT_PALETTE_HEX.forEach((hex, i) => { inputs[i].value = hex; });
|
||||
const inputs = paletteHexInputs();
|
||||
window.DEFAULT_PALETTE_HEX.forEach((hex, i) => {
|
||||
inputs[i].value = hex;
|
||||
syncPaletteFromHex(i);
|
||||
});
|
||||
savePalette(new URLSearchParams({ palette_reset: 'true' }));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user