feat(hp48): color-code keypad and inline error display

This commit is contained in:
2026-04-25 00:04:33 +02:00
parent e9a6ad49d1
commit 26814bee3c
+104 -50
View File
@@ -16,6 +16,20 @@
--key-text: #f2f2f2; --key-text: #f2f2f2;
--accent: #8cff6d; --accent: #8cff6d;
--border: #111; --border: #111;
--key-number: #5a5a5a;
--key-number-2: #444;
--key-arithmetic: #7f5c24;
--key-arithmetic-2: #5f4216;
--key-trig: #2d6d73;
--key-trig-2: #1f4f54;
--key-stack: #6b4a8f;
--key-stack-2: #4f356b;
--key-special: #3d5d8a;
--key-special-2: #2c4464;
--key-danger: #8a3f3f;
--key-danger-2: #632d2d;
--key-enter: #5d8a3d;
--key-enter-2: #45662e;
} }
* { box-sizing: border-box; } * { box-sizing: border-box; }
@@ -124,34 +138,6 @@
padding: 14px; padding: 14px;
} }
.title {
color: #fff;
margin: 0 0 10px;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.keypad-labels {
display: grid;
grid-template-columns: 4fr 3fr 2fr;
gap: 12px;
margin-bottom: 10px;
}
.keypad-label {
color: #d8d8d8;
font-size: 11px;
font-weight: bold;
letter-spacing: 0.08em;
text-transform: uppercase;
text-align: center;
padding: 6px 8px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.keypad-wrap { .keypad-wrap {
display: grid; display: grid;
grid-template-columns: minmax(0, 4fr) minmax(0, 3fr) minmax(0, 2fr); grid-template-columns: minmax(0, 4fr) minmax(0, 3fr) minmax(0, 2fr);
@@ -185,11 +171,41 @@
min-height: 46px; min-height: 46px;
} }
.keypad button.active-toggle { .keypad button.active-toggle,
.active-toggle {
background: linear-gradient(180deg, #6a8c52, #49693a); background: linear-gradient(180deg, #6a8c52, #49693a);
color: #fff; color: #fff;
} }
.key-number {
background: linear-gradient(180deg, var(--key-number), var(--key-number-2));
}
.key-arithmetic {
background: linear-gradient(180deg, var(--key-arithmetic), var(--key-arithmetic-2));
}
.key-trigonometry {
background: linear-gradient(180deg, var(--key-trig), var(--key-trig-2));
}
.key-stack {
background: linear-gradient(180deg, var(--key-stack), var(--key-stack-2));
}
.key-special {
background: linear-gradient(180deg, var(--key-special), var(--key-special-2));
}
.key-danger {
background: linear-gradient(180deg, var(--key-danger), var(--key-danger-2));
}
.key-enter {
background: linear-gradient(180deg, var(--key-enter), var(--key-enter-2));
font-weight: bold;
}
.keypad-group button.enter-tall { .keypad-group button.enter-tall {
grid-row: span 2; grid-row: span 2;
} }
@@ -230,12 +246,9 @@
min-height: 40px; min-height: 40px;
} }
.error { .display-error {
margin-top: 10px; color: #8a2a1c;
min-height: 20px; font-weight: bold;
color: #ff8a8a;
font-family: "Courier New", monospace;
font-size: 13px;
} }
@@ -244,7 +257,6 @@
padding: 16px; padding: 16px;
} }
.keypad-labels,
.keypad-wrap { .keypad-wrap {
gap: 8px; gap: 8px;
} }
@@ -265,7 +277,6 @@
border-radius: 16px; border-radius: 16px;
} }
.keypad-labels,
.keypad-wrap { .keypad-wrap {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
@@ -297,12 +308,6 @@
<input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1"> <input id="input" class="hidden-input" type="text" autocomplete="off" aria-hidden="true" tabindex="-1">
<div class="panel" id="keypadPanel"> <div class="panel" id="keypadPanel">
<div class="title">Calculator Keys</div>
<div class="keypad-labels" aria-hidden="true">
<div class="keypad-label">Functions</div>
<div class="keypad-label">Numbers</div>
<div class="keypad-label">Operators</div>
</div>
<div class="keypad-wrap"> <div class="keypad-wrap">
<div class="keypad-group functions" id="functionsKeypad"></div> <div class="keypad-group functions" id="functionsKeypad"></div>
<div class="keypad-group numbers" id="numbersKeypad"></div> <div class="keypad-group numbers" id="numbersKeypad"></div>
@@ -319,7 +324,6 @@
</div> </div>
</div> </div>
<div id="error" class="error"></div>
</div> </div>
</div> </div>
@@ -331,7 +335,6 @@
const stackEl = document.getElementById('stack'); const stackEl = document.getElementById('stack');
const displayEl = document.getElementById('display'); const displayEl = document.getElementById('display');
const errorEl = document.getElementById('error');
const modeLabel = document.getElementById('modeLabel'); const modeLabel = document.getElementById('modeLabel');
const functionsKeypadEl = document.getElementById('functionsKeypad'); const functionsKeypadEl = document.getElementById('functionsKeypad');
const numbersKeypadEl = document.getElementById('numbersKeypad'); const numbersKeypadEl = document.getElementById('numbersKeypad');
@@ -344,6 +347,7 @@
let openMenuName = null; let openMenuName = null;
let isMovingStackItem = false; let isMovingStackItem = false;
let stackSnapshotBeforeMove = null; let stackSnapshotBeforeMove = null;
let currentErrorMessage = '';
function getKeypadLayout() { function getKeypadLayout() {
return { return {
@@ -427,6 +431,42 @@
}; };
} }
function getKeyClassName(key) {
if (key.type === 'input') {
return 'key-number';
}
if (key.value === 'clear') {
return 'key-danger';
}
if (key.value === 'enter') {
return 'key-enter';
}
if (key.type === 'special') {
return 'key-special';
}
const stackCommands = new Set(['neg']);
const arithmeticCommands = new Set(['add', 'sub', 'mul', 'div', 'mod', 'pow', 'sqr', 'sqrt', 'recip', 'log', 'ln']);
const trigonometryCommands = new Set(['sin', 'cos', 'tan', 'asin', 'acos', 'atan']);
if (stackCommands.has(key.value)) {
return 'key-stack';
}
if (arithmeticCommands.has(key.value)) {
return 'key-arithmetic';
}
if (trigonometryCommands.has(key.value)) {
return 'key-trigonometry';
}
return '';
}
function renderKeypadGroup(groupEl, layout) { function renderKeypadGroup(groupEl, layout) {
groupEl.innerHTML = ''; groupEl.innerHTML = '';
const occupied = new Set(); const occupied = new Set();
@@ -449,6 +489,10 @@
const button = document.createElement('button'); const button = document.createElement('button');
button.textContent = key.label; button.textContent = key.label;
const keyClassName = getKeyClassName(key);
if (keyClassName) {
button.classList.add(keyClassName);
}
button.style.gridColumn = String(colIndex + 1); button.style.gridColumn = String(colIndex + 1);
button.style.gridRow = `${rowIndex + 1} / span ${key.rowspan || 1}`; button.style.gridRow = `${rowIndex + 1} / span ${key.rowspan || 1}`;
if (key.rowspan && key.rowspan > 1) { if (key.rowspan && key.rowspan > 1) {
@@ -591,7 +635,8 @@
execute(key.value); execute(key.value);
} }
} catch (error) { } catch (error) {
errorEl.textContent = error.message; currentErrorMessage = error.message;
render();
} }
} }
@@ -719,17 +764,23 @@
} }
stackEl.innerHTML = lines.join(''); stackEl.innerHTML = lines.join('');
if (calc.isEditing) { if (currentErrorMessage) {
displayEl.textContent = `ERROR: ${currentErrorMessage}`;
displayEl.classList.add('display-error');
} else if (calc.isEditing) {
displayEl.textContent = `ENTERING: ${calc.inputValue}`; displayEl.textContent = `ENTERING: ${calc.inputValue}`;
displayEl.classList.remove('display-error');
} else if (isMovingStackItem && hasStackSelection()) { } else if (isMovingStackItem && hasStackSelection()) {
displayEl.textContent = `MOVING: ${['X', 'Y', 'Z', 'T'][stackCursor] || '?'}`; displayEl.textContent = `MOVING: ${['X', 'Y', 'Z', 'T'][stackCursor] || '?'}`;
displayEl.classList.remove('display-error');
} else if (hasStackSelection()) { } else if (hasStackSelection()) {
displayEl.textContent = `SELECTED: ${['X', 'Y', 'Z', 'T'][stackCursor] || '?'}`; displayEl.textContent = `SELECTED: ${['X', 'Y', 'Z', 'T'][stackCursor] || '?'}`;
displayEl.classList.remove('display-error');
} else { } else {
displayEl.textContent = 'READY'; displayEl.textContent = 'READY';
displayEl.classList.remove('display-error');
} }
modeLabel.textContent = calc.angleMode; modeLabel.textContent = calc.angleMode;
errorEl.textContent = '';
renderKeypad(); renderKeypad();
} }
@@ -773,10 +824,12 @@
clearStackSelection(); clearStackSelection();
calc.command(name); calc.command(name);
} }
currentErrorMessage = '';
syncInputFromState(); syncInputFromState();
render(); render();
} catch (error) { } catch (error) {
errorEl.textContent = error.message; currentErrorMessage = error.message;
render();
} }
} }
@@ -960,7 +1013,8 @@
execute(action.value); execute(action.value);
} }
} catch (error) { } catch (error) {
errorEl.textContent = error.message; currentErrorMessage = error.message;
render();
} }
} }