Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions apps/overlay/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@
.setting-row input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.setting-row input[type="color"]::-webkit-color-swatch { border: none; border-radius: 2px; }

/* Text entry beside each color swatch (#91): type a hex (#RGB / #RRGGBB) or
rgb(r,g,b) value instead of using the native picker. The swatch and text
field stay in sync. */
.color-combo { display: flex; align-items: center; gap: 6px; }
.color-text {
width: 78px;
background: rgba(255,255,255,0.08);
color: #eee;
border: 1px solid rgba(255,255,255,0.12);
border-radius: 4px;
padding: 3px 5px;
font: 11px ui-monospace, Consolas, 'Courier New', monospace;
text-transform: lowercase;
}
.color-text:focus { outline: none; border-color: #3388ff; }
.color-text.invalid { border-color: #ff5555; color: #ff9999; }
/* Grouped color rows (Roll / Axis): label on its own line, then a full-width
row of swatch-over-text cells so all three fit the narrow panel. */
.setting-row.color-row-stacked { flex-direction: column; align-items: stretch; gap: 6px; }
.color-group { display: flex; gap: 8px; }
.color-group .color-combo { flex: 1; min-width: 0; flex-direction: column; gap: 3px; align-items: stretch; }
.color-group .color-combo input[type="color"] { width: 100%; }
.color-group .color-text { width: 100%; text-align: center; }

.setting-row input[type="checkbox"] {
width: 14px; height: 14px;
accent-color: #3388ff;
Expand Down Expand Up @@ -1020,9 +1044,9 @@ <h3>Window Display</h3>
<label>Show Roll HUD</label>
<input type="checkbox" id="show-roll-hud" autocomplete="off">
</div>
<div class="setting-row">
<div class="setting-row color-row-stacked">
<label title="Roll HUD needle/label colors by lean band (near-center / mid / high lean).">Roll Colors (Normal / Mid / High)</label>
<span style="display:flex; gap:4px;">
<span class="color-group">
<input type="color" id="lean-color-normal" value="#ffffff" title="Normal (near center)">
<input type="color" id="lean-color-mid" value="#ffaa22" title="Mid lean">
<input type="color" id="lean-color-high" value="#ff4444" title="High lean">
Expand All @@ -1036,9 +1060,9 @@ <h3>Window Display</h3>
<label>Show Axis HUD</label>
<input type="checkbox" id="show-axis-readout">
</div>
<div class="setting-row">
<div class="setting-row color-row-stacked">
<label title="Per-axis colors for the Pitch / Roll / Yaw readout.">Axis Colors (Pitch / Roll / Yaw)</label>
<span style="display:flex; gap:4px;">
<span class="color-group">
<input type="color" id="axis-color-pitch" value="#44dd66" title="Pitch">
<input type="color" id="axis-color-roll" value="#ee4455" title="Roll">
<input type="color" id="axis-color-yaw" value="#4488ff" title="Yaw">
Expand Down
62 changes: 62 additions & 0 deletions apps/overlay/src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2236,6 +2236,68 @@ function applyGreenScreen() {
greenScreenToggle.addEventListener('change', applyGreenScreen);
greenScreenColorInput.addEventListener('input', applyGreenScreen);

// ── Type-a-color (#91) ────────────────────────────────────────────────────
// Every color setting is a native <input type="color"> (swatch-only). Let the
// user also type a value: accept hex (#RGB / #RRGGBB) or rgb(r,g,b) / rgba(...),
// normalize to #rrggbb, and on a valid parse write it back to the swatch and
// dispatch the swatch's 'input' event — so every existing per-picker apply +
// persist handler runs unchanged. Picker→text sync keeps the field current when
// the native swatch is used. Runs last so each swatch's value is already
// initialized from localStorage before we mirror it into the text field.
function normalizeColor(value) {
if (value == null) return null;
const v = String(value).trim().toLowerCase();
let m = v.match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/);
if (m) {
let h = m[1];
if (h.length === 3) h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
return '#' + h;
}
m = v.match(/^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*[\d.]+\s*)?\)$/);
if (m) {
const c = [m[1], m[2], m[3]].map(Number);
if (c.every((n) => n <= 255)) return '#' + c.map((n) => n.toString(16).padStart(2, '0')).join('');
}
return null;
}

function attachColorText(colorInput) {
const combo = document.createElement('span');
combo.className = 'color-combo';
const text = document.createElement('input');
text.type = 'text';
text.className = 'color-text';
text.spellcheck = false;
text.autocomplete = 'off';
text.setAttribute('aria-label', (colorInput.id || 'color') + ' hex or rgb value');
text.value = colorInput.value;
// Wrap the swatch: text field first, swatch second (CSS stacks them for the
// grouped rows). Moving the swatch in the DOM keeps its event listeners.
colorInput.parentNode.insertBefore(combo, colorInput);
combo.appendChild(text);
combo.appendChild(colorInput);

const commit = () => {
const hex = normalizeColor(text.value);
if (!hex) { text.classList.add('invalid'); return; }
text.classList.remove('invalid');
text.value = hex;
if (colorInput.value.toLowerCase() !== hex) {
colorInput.value = hex;
colorInput.dispatchEvent(new Event('input', { bubbles: true }));
}
};
text.addEventListener('change', commit);
text.addEventListener('keydown', (e) => { if (e.key === 'Enter') { commit(); text.blur(); } });
// Native swatch moved → reflect it in the text field.
colorInput.addEventListener('input', () => {
text.value = colorInput.value;
text.classList.remove('invalid');
});
}

document.querySelectorAll('#settings-panel input[type="color"]').forEach(attachColorText);

// Camera presets — one selected at a time, used as calibration view.
// Defaults to Top for every controller; the last-selected preset is persisted
// (issue #70) so reopening the overlay restores the user's preferred view
Expand Down
Loading