diff --git a/public/css/tools/binary.css b/public/css/tools/binary.css deleted file mode 100644 index f300e8f..0000000 --- a/public/css/tools/binary.css +++ /dev/null @@ -1,137 +0,0 @@ -/* ---------- DSEG7 font ---------- */ -/* Put your font file here: - public/fonts/DSEG7Classic-Regular.woff2 -*/ -@font-face { - font-family: "DSEG7ClassicRegular"; - src: url("/fonts/DSEG7Classic-Regular.woff2") format("woff2"); - font-display: swap; -} - -/* ---------- Layout ---------- */ -.tool-shell { - min-height: 100vh; - display: grid; - place-items: center; - padding: 1rem; - background: #0b0f14; - color: #e7eaf0; - font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; -} - -.tool-card { - width: min(1100px, 100%); - background: #111824; - border: 1px solid rgba(255,255,255,0.08); - border-radius: 18px; - padding: 1rem; -} - -.tool-header h1 { margin: 0 0 .25rem 0; font-size: 1.4rem; } -.tool-header p { margin: 0 0 1rem 0; opacity: .85; } - -.display-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: .75rem; - margin-bottom: .75rem; -} - -.display-box { - background: #0b0f14; - border: 1px solid rgba(255,255,255,0.08); - border-radius: 14px; - padding: .75rem; -} - -.display-label { font-size: .9rem; opacity: .8; margin-bottom: .25rem; } - -.sevenseg { - font-family: "DSEG7ClassicRegular", monospace; - font-size: clamp(2rem, 4vw, 3.2rem); - letter-spacing: 0.08em; - line-height: 1.1; -} - -/* Buttons under denary/binary (your request) */ -.actions { - display: flex; - flex-wrap: wrap; - gap: .5rem; - margin-bottom: 1rem; -} - -/* ---------- Simple MD3-ish buttons ---------- */ -.md3-btn { - border: 1px solid rgba(255,255,255,0.16); - background: rgba(255,255,255,0.06); - color: #e7eaf0; - padding: .6rem .9rem; - border-radius: 999px; - cursor: pointer; - font-weight: 600; -} -.md3-btn:hover { background: rgba(255,255,255,0.10); } -.md3-btn--tonal { background: rgba(255,255,255,0.10); } - -/* ---------- Switches row ---------- */ -.switch-row { - display: grid; - grid-template-columns: repeat(8, minmax(90px, 1fr)); - gap: .75rem; -} - -.switch-col { - display: grid; - justify-items: center; - gap: .35rem; -} - -.bit-label { opacity: .85; font-weight: 600; } - -/* ---------- “Light switch” rocker ---------- */ -.rocker { - position: relative; - width: 70px; - height: 46px; - display: inline-block; - user-select: none; -} - -.rocker input { - opacity: 0; - width: 0; - height: 0; -} - -.rocker-body { - position: absolute; - inset: 0; - border-radius: 12px; - background: #1a2331; - border: 1px solid rgba(255,255,255,0.14); - box-shadow: inset 0 0 0 2px rgba(0,0,0,0.35); -} - -/* the “toggle” */ -.rocker-body::after { - content: ""; - position: absolute; - left: 6px; - top: 6px; - width: 58px; - height: 18px; - border-radius: 10px; - background: rgba(255,255,255,0.20); - transition: transform 180ms ease, background 180ms ease; -} - -/* ON position */ -.rocker input:checked + .rocker-body::after { - transform: translateY(16px); - background: rgba(255,255,255,0.55); -} - -@media (max-width: 900px) { - .switch-row { grid-template-columns: repeat(4, minmax(90px, 1fr)); } -} diff --git a/public/favicon.svg b/public/favicon.svg deleted file mode 100644 index f157bd1..0000000 --- a/public/favicon.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - diff --git a/public/js/binary.js b/public/js/binary.js deleted file mode 100644 index 0e65497..0000000 --- a/public/js/binary.js +++ /dev/null @@ -1,321 +0,0 @@ -// Binary simulator: unsigned + two's complement, 4–64 bits. -// Key fixes: -// - CSS moved to /public so dynamically-created switches & bulbs are styled. -// - Bits grid wraps into rows of 8 (CSS). -// - Binary readout wraps every 8 bits (JS -> adds \n). - -const bitsGrid = document.getElementById("bitsGrid"); -const denaryEl = document.getElementById("denaryNumber"); -const binaryEl = document.getElementById("binaryNumber"); - -const modeToggle = document.getElementById("modeToggle"); -const modeHint = document.getElementById("modeHint"); - -const bitsInput = document.getElementById("bitsInput"); -const btnUp = document.getElementById("btnBitsUp"); -const btnDown = document.getElementById("btnBitsDown"); - -const btnShiftL = document.getElementById("btnShiftLeft"); -const btnShiftR = document.getElementById("btnShiftRight"); -const btnCustBin = document.getElementById("btnCustomBinary"); -const btnCustDen = document.getElementById("btnCustomDenary"); - -let bitCount = clampInt(Number(bitsInput.value || 8), 4, 64); -bitsInput.value = String(bitCount); - -let isTwos = false; - -// bits[0] = MSB, bits[bitCount-1] = LSB -let bits = new Array(bitCount).fill(false); - -/* ----------------------------- - Helpers ------------------------------ */ -function clampInt(n, min, max){ - n = Number(n); - if (!Number.isFinite(n)) return min; - n = Math.floor(n); - return Math.max(min, Math.min(max, n)); -} - -function maxUnsigned(nBits){ - // nBits up to 64 -> use BigInt for correctness - return (1n << BigInt(nBits)) - 1n; -} - -function rangeTwos(nBits){ - const min = -(1n << BigInt(nBits - 1)); - const max = (1n << BigInt(nBits - 1)) - 1n; - return { min, max }; -} - -function bitsToBigIntUnsigned(){ - let v = 0n; - for (let i = 0; i < bitCount; i++){ - v = (v << 1n) + (bits[i] ? 1n : 0n); - } - return v; -} - -function bitsToBigIntTwos(){ - // Interpret current bit pattern as signed two's complement. - const unsigned = bitsToBigIntUnsigned(); - const signBit = bits[0] ? 1n : 0n; - - if (signBit === 0n) return unsigned; // positive - - // negative: unsigned - 2^n - const mod = 1n << BigInt(bitCount); - return unsigned - mod; -} - -function bigIntToBitsUnsigned(v){ - // v assumed 0..2^n-1 - const out = new Array(bitCount).fill(false); - let x = BigInt(v); - for (let i = bitCount - 1; i >= 0; i--){ - out[i] = (x & 1n) === 1n; - x >>= 1n; - } - return out; -} - -function bigIntToBitsTwos(v){ - // v assumed in signed range; convert to 0..2^n-1 representation - const mod = 1n << BigInt(bitCount); - let x = BigInt(v); - if (x < 0n) x = mod + x; - return bigIntToBitsUnsigned(x); -} - -function formatBinaryForReadout(){ - // Wrap every 8 bits into a new line; keep spaces between groups. - const raw = bits.map(b => (b ? "1" : "0")).join(""); - const groupsOf8 = raw.match(/.{1,8}/g) || [raw]; - return groupsOf8.join("\n"); -} - -/* ----------------------------- - UI build ------------------------------ */ -function buildBitsGrid(){ - bitsGrid.innerHTML = ""; - - for (let i = 0; i < bitCount; i++){ - const weightUnsigned = 1n << BigInt(bitCount - 1 - i); - const isMSB = i === 0; - - const bitEl = document.createElement("div"); - bitEl.className = "bit"; - - const bulb = document.createElement("div"); - bulb.className = "bulb"; - bulb.id = `bulb-${i}`; - bulb.setAttribute("aria-hidden", "true"); - - const val = document.createElement("div"); - val.className = "bitVal"; - // if in two's complement, show MSB as negative label visually - if (isTwos && isMSB) val.classList.add("msbNeg"); - val.textContent = weightUnsigned.toString(); // show magnitude only ( "-" is via CSS ) - - const label = document.createElement("label"); - label.className = "switch"; - label.setAttribute("aria-label", `Toggle bit ${i + 1}`); - - const input = document.createElement("input"); - input.type = "checkbox"; - input.dataset.index = String(i); - - const slider = document.createElement("span"); - slider.className = "slider"; - - label.appendChild(input); - label.appendChild(slider); - - bitEl.appendChild(bulb); - bitEl.appendChild(val); - bitEl.appendChild(label); - - bitsGrid.appendChild(bitEl); - } - - // Hook listeners after build - bitsGrid.querySelectorAll('input[type="checkbox"][data-index]').forEach(input => { - input.addEventListener("change", () => { - const idx = Number(input.dataset.index); - bits[idx] = input.checked; - updateAll(); - }); - }); - - syncInputsToBits(); - updateAll(); -} - -function syncInputsToBits(){ - bitsGrid.querySelectorAll('input[type="checkbox"][data-index]').forEach(input => { - const idx = Number(input.dataset.index); - input.checked = !!bits[idx]; - }); -} - -function syncBulbsToBits(){ - for (let i = 0; i < bitCount; i++){ - const bulb = document.getElementById(`bulb-${i}`); - if (bulb) bulb.classList.toggle("on", !!bits[i]); - } -} - -function updateModeHint(){ - modeHint.textContent = isTwos - ? "Tip: In two’s complement, the left-most bit (MSB) represents a negative value." - : "Tip: In unsigned binary, all bits represent positive values."; -} - -function updateAll(){ - const denary = isTwos ? bitsToBigIntTwos() : bitsToBigIntUnsigned(); - denaryEl.textContent = denary.toString(); - binaryEl.textContent = formatBinaryForReadout(); - syncBulbsToBits(); -} - -/* ----------------------------- - Bit-count changes (preserve LSBs) ------------------------------ */ -function setBitCount(newCount){ - newCount = clampInt(newCount, 4, 64); - if (newCount === bitCount) return; - - // preserve LSB-aligned pattern: - // take from the right end of old bits, pad on the left with zeros. - const old = bits.slice(); - const newBits = new Array(newCount).fill(false); - - const take = Math.min(bitCount, newCount); - for (let i = 0; i < take; i++){ - // copy from LSB side - newBits[newCount - 1 - i] = old[bitCount - 1 - i]; - } - - bitCount = newCount; - bits = newBits; - bitsInput.value = String(bitCount); - - buildBitsGrid(); // rebuild with correct styling + rows -} - -/* ----------------------------- - Custom input ------------------------------ */ -function requestBinary(){ - const v = prompt(`Enter a ${bitCount}-bit binary number (0/1):`); - if (v === null) return; - - const clean = v.replace(/\s+/g, ""); - if (!/^[01]+$/.test(clean)){ - alert("Invalid input. Use only 0 and 1."); - return; - } - - const padded = clean.slice(-bitCount).padStart(bitCount, "0"); - bits = [...padded].map(ch => ch === "1"); - syncInputsToBits(); - updateAll(); -} - -function requestDenary(){ - const promptText = isTwos - ? `Enter a denary number (${rangeTwos(bitCount).min} to ${rangeTwos(bitCount).max}):` - : `Enter a denary number (0 to ${maxUnsigned(bitCount)}):`; - - const raw = prompt(promptText); - if (raw === null) return; - - // allow leading +/- and digits - if (!/^[+-]?\d+$/.test(raw.trim())){ - alert("Invalid input. Enter a whole number."); - return; - } - - const n = BigInt(raw.trim()); - - if (isTwos){ - const { min, max } = rangeTwos(bitCount); - if (n < min || n > max){ - alert(`Out of range. Enter between ${min} and ${max}.`); - return; - } - bits = bigIntToBitsTwos(n); - } else { - const maxU = maxUnsigned(bitCount); - if (n < 0n || n > maxU){ - alert(`Out of range. Enter between 0 and ${maxU}.`); - return; - } - bits = bigIntToBitsUnsigned(n); - } - - syncInputsToBits(); - updateAll(); -} - -/* ----------------------------- - Shifts ------------------------------ */ -function shiftLeft(){ - bits.shift(); - bits.push(false); - syncInputsToBits(); - updateAll(); -} - -function shiftRight(){ - if (isTwos){ - // arithmetic shift right (preserve sign bit) - const sign = bits[0]; - bits.pop(); - bits.unshift(sign); - } else { - // logical shift right - bits.pop(); - bits.unshift(false); - } - syncInputsToBits(); - updateAll(); -} - -/* ----------------------------- - Mode toggle ------------------------------ */ -function setModeTwos(on){ - isTwos = !!on; - updateModeHint(); - - // rebuild so MSB label shows "-" via CSS class - // (and keeps the same bit pattern) - buildBitsGrid(); -} - -/* ----------------------------- - Wire up UI controls ------------------------------ */ -modeToggle.addEventListener("change", () => setModeTwos(modeToggle.checked)); - -btnUp.addEventListener("click", () => setBitCount(bitCount + 1)); -btnDown.addEventListener("click", () => setBitCount(bitCount - 1)); - -bitsInput.addEventListener("change", () => setBitCount(Number(bitsInput.value))); - -btnShiftL.addEventListener("click", shiftLeft); -btnShiftR.addEventListener("click", shiftRight); - -btnCustBin.addEventListener("click", requestBinary); -btnCustDen.addEventListener("click", requestDenary); - -/* ----------------------------- - Init ------------------------------ */ -updateModeHint(); -buildBitsGrid(); -updateAll(); diff --git a/public/scripts/binary.js b/public/scripts/binary.js index 8effa3c..4733635 100644 --- a/public/scripts/binary.js +++ b/public/scripts/binary.js @@ -1,11 +1,4 @@ -/* Binary simulator for Computing:Box - - Wrap bits every 8 (CSS handles layout) - - Bit width 4..64 - - Unsigned + Two’s complement toggle (WORKING) - - Bulbs + toggle switches for each bit -*/ - -const bitsGrid = document.getElementById("bitsGrid"); +const bitsRows = document.getElementById("bitsRows"); const denaryEl = document.getElementById("denaryNumber"); const binaryEl = document.getElementById("binaryNumber"); @@ -21,281 +14,250 @@ const btnShiftRight = document.getElementById("btnShiftRight"); const btnCustomBinary = document.getElementById("btnCustomBinary"); const btnCustomDenary = document.getElementById("btnCustomDenary"); -let bitCount = clampInt(Number(bitsInput?.value ?? 8), 4, 64); -let isTwos = false; +let bitCount = clampInt(Number(bitsInput.value || 8), 1, 64); +let bits = new Array(bitCount).fill(false); // index 0 = MSB -// bits[0] is MSB, bits[bitCount-1] is LSB -let bits = new Array(bitCount).fill(false); - -function clampInt(n, min, max) { +function clampInt(n, min, max){ n = Number(n); - if (!Number.isInteger(n)) n = min; + if (!Number.isFinite(n)) n = min; + n = Math.floor(n); return Math.max(min, Math.min(max, n)); } -function pow2(exp) { - // safe up to 2^63 in JS integer precision? (JS uses float) but our usage is display/control, ok. - return 2 ** exp; +function isTwos(){ + return !!modeToggle.checked; } -/* ----------------------------- - Build UI (bulbs + switches) ------------------------------ */ -function buildBits(count) { - bitCount = clampInt(count, 4, 64); - bits = resizeBits(bits, bitCount); +function msbValue(){ + return 2 ** (bitCount - 1); +} - bitsGrid.innerHTML = ""; +function unsignedValueAt(i){ + // i=0 is MSB + return 2 ** (bitCount - 1 - i); +} - for (let i = 0; i < bitCount; i++) { - const exp = bitCount - 1 - i; // MSB has highest exponent - const value = pow2(exp); +function computeUnsigned(){ + let sum = 0; + for (let i = 0; i < bitCount; i++){ + if (bits[i]) sum += unsignedValueAt(i); + } + return sum; +} - const bit = document.createElement("div"); - bit.className = "bit"; - bit.innerHTML = ` - -
${value}
- - `; +function computeDenary(){ + const u = computeUnsigned(); + if (!isTwos()) return u; - bitsGrid.appendChild(bit); + // Two's complement: + // if MSB is 1, value = unsigned - 2^n + if (bits[0]) return u - (2 ** bitCount); + return u; +} + +function bitsToString(){ + return bits.map(b => (b ? "1" : "0")).join(""); +} + +function updateModeHint(){ + if (isTwos()){ + modeHint.textContent = "Tip: In two's complement, the left-most bit (MSB) represents a negative value."; + } else { + modeHint.textContent = "Tip: In unsigned binary, all bits represent positive values."; + } +} + +function buildBitsUI(){ + bitsRows.innerHTML = ""; + + // Build rows of 8 bits + const rowCount = Math.ceil(bitCount / 8); + + for (let r = 0; r < rowCount; r++){ + const row = document.createElement("div"); + row.className = "byteRow"; + + const start = r * 8; + const end = Math.min(start + 8, bitCount); + + for (let i = start; i < end; i++){ + const bitEl = document.createElement("div"); + bitEl.className = "bit"; + + // label: show -MSB in two's complement + const labelVal = (isTwos() && i === 0) ? -msbValue() : unsignedValueAt(i); + + bitEl.innerHTML = ` + +
${labelVal}
+ + `; + + row.appendChild(bitEl); + } + + bitsRows.appendChild(row); } - hookSwitches(); - syncUI(); -} - -function resizeBits(oldBits, newCount) { - // keep LSB end stable when changing bit width: - // align old bits to the right (LSB) - const out = new Array(newCount).fill(false); - const copy = Math.min(oldBits.length, newCount); - - for (let k = 0; k < copy; k++) { - // copy from end (LSB) - out[newCount - 1 - k] = oldBits[oldBits.length - 1 - k]; - } - return out; -} - -function hookSwitches() { - bitsGrid.querySelectorAll('input[type="checkbox"][data-index]').forEach((input) => { + // Hook switches + bitsRows.querySelectorAll('input[type="checkbox"][data-index]').forEach(input => { input.addEventListener("change", () => { const i = Number(input.dataset.index); bits[i] = input.checked; updateReadout(); - updateBulb(i); }); }); + + syncUI(); } -/* ----------------------------- - Mode toggle (Unsigned <-> Two’s) ------------------------------ */ -function setModeTwos(on) { - isTwos = !!on; - - modeHint.textContent = isTwos - ? "Tip: In two’s complement, the left-most bit (MSB) represents a negative value." - : "Tip: In unsigned binary, all bits represent positive values."; - - // Just re-calc denary using current bit pattern - updateReadout(); -} - -modeToggle?.addEventListener("change", () => setModeTwos(modeToggle.checked)); - -/* ----------------------------- - Calculations ------------------------------ */ -function getUnsignedValue() { - let n = 0; - for (let i = 0; i < bitCount; i++) { - if (!bits[i]) continue; - const exp = bitCount - 1 - i; - n += pow2(exp); - } - return n; -} - -function getTwosValue() { - // MSB has negative weight - let n = 0; - for (let i = 0; i < bitCount; i++) { - if (!bits[i]) continue; - const exp = bitCount - 1 - i; - if (i === 0) n -= pow2(exp); // MSB - else n += pow2(exp); - } - return n; -} - -function getDenary() { - return isTwos ? getTwosValue() : getUnsignedValue(); -} - -function getBinaryString() { - return bits.map(b => (b ? "1" : "0")).join(""); -} - -/* ----------------------------- - UI updates ------------------------------ */ -function updateBulb(i) { - const bulb = document.getElementById(`bulb-${i}`); - if (bulb) bulb.classList.toggle("on", bits[i]); -} - -function updateReadout() { - denaryEl.textContent = String(getDenary()); - binaryEl.textContent = getBinaryString(); -} - -function syncUI() { - bitsGrid.querySelectorAll('input[type="checkbox"][data-index]').forEach((input) => { +function syncUI(){ + // sync inputs + bitsRows.querySelectorAll('input[type="checkbox"][data-index]').forEach(input => { const i = Number(input.dataset.index); input.checked = !!bits[i]; - updateBulb(i); }); + + // sync bulbs + for (let i = 0; i < bitCount; i++){ + const bulb = document.getElementById(`bulb-${i}`); + if (bulb) bulb.classList.toggle("on", !!bits[i]); + } + updateReadout(); } -/* ----------------------------- - Set from Binary / Denary ------------------------------ */ -function setFromBinary(bin) { +function updateReadout(){ + denaryEl.textContent = String(computeDenary()); + binaryEl.textContent = bitsToString(); +} + +function setFromBinary(bin){ const clean = String(bin).replace(/\s+/g, ""); if (!/^[01]+$/.test(clean)) return false; const padded = clean.slice(-bitCount).padStart(bitCount, "0"); - bits = [...padded].map(ch => ch === "1"); + for (let i = 0; i < bitCount; i++){ + bits[i] = padded[i] === "1"; + } + syncUI(); return true; } -function setFromDenary(n) { +function setFromDenary(n){ + n = Number(n); if (!Number.isInteger(n)) return false; - if (!isTwos) { - const min = 0; - const max = pow2(bitCount) - 1; - if (n < min || n > max) return false; + if (!isTwos()){ + // unsigned: 0 .. (2^n - 1) + const max = (2 ** bitCount) - 1; + if (n < 0 || n > max) return false; - // unsigned fill from MSB->LSB - let remaining = n; - bits = bits.map((_, i) => { - const exp = bitCount - 1 - i; - const value = pow2(exp); - if (remaining >= value) { - remaining -= value; - return true; + for (let i = 0; i < bitCount; i++){ + const v = unsignedValueAt(i); + if (n >= v){ + bits[i] = true; + n -= v; + } else { + bits[i] = false; } - return false; - }); - + } syncUI(); return true; } - // Two's complement bounds - const min = -pow2(bitCount - 1); - const max = pow2(bitCount - 1) - 1; + // two's complement: -(2^(n-1)) .. (2^(n-1)-1) + const min = -(2 ** (bitCount - 1)); + const max = (2 ** (bitCount - 1)) - 1; if (n < min || n > max) return false; - // Convert to raw unsigned representation: - // if negative, represent as 2^bitCount + n - let raw = n; - if (raw < 0) raw = pow2(bitCount) + raw; + // convert to unsigned representation + let u = n; + if (u < 0) u = (2 ** bitCount) + u; // wrap - let remaining = raw; - bits = bits.map((_, i) => { - const exp = bitCount - 1 - i; - const value = pow2(exp); - if (remaining >= value) { - remaining -= value; - return true; + for (let i = 0; i < bitCount; i++){ + const v = unsignedValueAt(i); + if (u >= v){ + bits[i] = true; + u -= v; + } else { + bits[i] = false; } - return false; - }); + } syncUI(); return true; } -/* ----------------------------- - Shifts ------------------------------ */ -function shiftLeft() { - // drop MSB, append 0 at LSB - bits.shift(); - bits.push(false); +function shiftLeft(){ + bits.shift(); // drop MSB + bits.push(false); // add LSB syncUI(); } -function shiftRight() { - // unsigned: logical shift right (prepend 0) - // twos: arithmetic shift right (prepend old MSB) - const msb = bits[0]; - bits.pop(); - bits.unshift(isTwos ? msb : false); +function shiftRight(){ + bits.pop(); // drop LSB + bits.unshift(false); // add MSB syncUI(); } -/* ----------------------------- - Bit width controls ------------------------------ */ -function applyBitCount(next) { - const v = clampInt(next, 4, 64); - bitsInput.value = String(v); - buildBits(v); +function setBitCount(newCount){ + newCount = clampInt(newCount, 4, 64); + if (newCount === bitCount) return; + + // preserve right-most bits (LSB side) when resizing + const old = bits.slice(); + const next = new Array(newCount).fill(false); + + const copy = Math.min(old.length, next.length); + for (let k = 0; k < copy; k++){ + // copy from end (LSB) + next[newCount - 1 - k] = old[old.length - 1 - k]; + } + + bitCount = newCount; + bits = next; + + bitsInput.value = String(bitCount); + buildBitsUI(); } -btnBitsUp?.addEventListener("click", () => applyBitCount(bitCount + 1)); -btnBitsDown?.addEventListener("click", () => applyBitCount(bitCount - 1)); +/* -------------------- events -------------------- */ -bitsInput?.addEventListener("change", () => { - applyBitCount(Number(bitsInput.value)); +modeToggle.addEventListener("change", () => { + updateModeHint(); + buildBitsUI(); // rebuild labels (MSB becomes negative/positive) }); -/* ----------------------------- - Buttons ------------------------------ */ -btnShiftLeft?.addEventListener("click", shiftLeft); -btnShiftRight?.addEventListener("click", shiftRight); +btnBitsUp.addEventListener("click", () => setBitCount(bitCount + 1)); +btnBitsDown.addEventListener("click", () => setBitCount(bitCount - 1)); -btnCustomBinary?.addEventListener("click", () => { - const v = prompt(`Enter a ${bitCount}-bit binary number:`); +bitsInput.addEventListener("change", () => setBitCount(Number(bitsInput.value))); + +btnShiftLeft.addEventListener("click", shiftLeft); +btnShiftRight.addEventListener("click", shiftRight); + +btnCustomBinary.addEventListener("click", () => { + const v = prompt(`Enter ${bitCount}-bit binary:`); if (v === null) return; - if (!setFromBinary(v)) alert("Invalid input. Use only 0 and 1."); + if (!setFromBinary(v)) alert("Invalid binary. Use only 0 and 1."); }); -btnCustomDenary?.addEventListener("click", () => { - const min = isTwos ? -pow2(bitCount - 1) : 0; - const max = isTwos ? (pow2(bitCount - 1) - 1) : (pow2(bitCount) - 1); +btnCustomDenary.addEventListener("click", () => { + const min = isTwos() ? -(2 ** (bitCount - 1)) : 0; + const max = isTwos() ? (2 ** (bitCount - 1)) - 1 : (2 ** bitCount) - 1; const v = prompt(`Enter a denary number (${min} to ${max}):`); if (v === null) return; - - const n = Number(v); - if (!Number.isInteger(n) || !setFromDenary(n)) { - alert(`Invalid input. Enter an integer from ${min} to ${max}.`); - } + if (!setFromDenary(Number(v))) alert("Invalid denary for current mode/bit width."); }); -/* ----------------------------- - INIT ------------------------------ */ -function init() { - // default mode: unsigned - setModeTwos(false); - modeToggle.checked = false; +/* -------------------- init -------------------- */ - // build initial bits - applyBitCount(bitCount); -} - -init(); +bitsInput.value = String(bitCount); +updateModeHint(); +buildBitsUI(); diff --git a/public/styles.css b/public/styles.css deleted file mode 100644 index 50ca124..0000000 --- a/public/styles.css +++ /dev/null @@ -1,96 +0,0 @@ -/* src/styles/md3-tokens.css */ -/* MD3-inspired tokens tuned for education: high readability, clear contrast, calm surfaces */ -:root{ - /* Typography */ - --font-sans: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; - /* Spacing + shape */ - --radius-1: 10px; - --radius-2: 16px; - --radius-3: 22px; - --shadow-1: 0 1px 2px rgba(0,0,0,.08), 0 2px 8px rgba(0,0,0,.06); - /* Color roles (keep simple) */ - --md-surface: #ffffff; - --md-surface-2: #f6f7fb; - --md-on-surface: #111318; - --md-primary: #2f6fed; /* calm blue */ - --md-on-primary: #ffffff; - --md-secondary: #5a5f72; /* muted */ - --md-on-secondary: #ffffff; - --md-tertiary: #0f766e; /* teal for "practical" tools */ - --md-on-tertiary: #ffffff; - --md-outline: #d7dbe7; - --md-success: #1a7f37; - --md-warning: #b54708; - --md-danger: #b42318; - /* Focus ring for accessibility */ - --md-focus: 0 0 0 3px rgba(47,111,237,.28); -} -@media (prefers-color-scheme: dark){ - :root{ - --md-surface: #0b0e14; - --md-surface-2: #121725; - --md-on-surface: #e8eaf2; - --md-primary: #9bb6ff; - --md-on-primary: #0b0e14; - --md-secondary: #b8bccd; - --md-on-secondary: #0b0e14; - --md-tertiary: #4fd1c5; - --md-on-tertiary: #0b0e14; - --md-outline: #2b3244; - --md-focus: 0 0 0 3px rgba(155,182,255,.25); - } -} - -/* src/styles/base.css */ -@import "./md3-tokens.css"; -html, body{ height:100%; } -body{ - margin:0; - font-family: var(--font-sans); - background: var(--md-surface-2); - color: var(--md-on-surface); -} -a{ color: var(--md-primary); text-decoration: none; } -a:hover{ text-decoration: underline; } -.container{ - max-width: 1100px; - margin: 0 auto; - padding: 16px; -} -.card{ - background: var(--md-surface); - border: 1px solid var(--md-outline); - border-radius: var(--radius-2); - box-shadow: var(--shadow-1); - padding: 16px; -} -.btn{ - display:inline-flex; - gap:8px; - align-items:center; - justify-content:center; - border-radius: 999px; - border: 1px solid var(--md-outline); - background: var(--md-surface); - color: var(--md-on-surface); - padding: 10px 14px; - font-weight: 600; - cursor: pointer; -} -.btn:hover{ filter: brightness(0.98); } -.btn:focus{ outline:none; box-shadow: var(--md-focus); } -.btn-primary{ - background: var(--md-primary); - color: var(--md-on-primary); - border-color: transparent; -} -.badge{ - display:inline-block; - padding: 2px 10px; - border-radius: 999px; - font-size: 12px; - border: 1px solid var(--md-outline); - background: var(--md-surface-2); -} -code, pre{ font-family: var(--font-mono); } diff --git a/public/css/binary.css b/public/styles/binary.css similarity index 73% rename from public/css/binary.css rename to public/styles/binary.css index 645c5b7..af70bde 100644 --- a/public/css/binary.css +++ b/public/styles/binary.css @@ -1,14 +1,4 @@ -:root{ - --bg: #1f2027; - --panel2: rgba(255,255,255,.04); - --text: #e8e8ee; - --muted: #a9acb8; - --accent: #33ff7a; - --accent-dim: rgba(51,255,122,.15); - --line: rgba(255,255,255,.12); -} - -/* DSEG font (you already have these in public/fonts/) */ +/* Font */ @font-face{ font-family: "DSEG7ClassicRegular"; src: @@ -19,10 +9,8 @@ font-display: swap; } -.wrap{ - max-width: 1200px; - margin: 0 auto; - padding: 32px 20px 60px; +.binaryWrap{ + padding-top: 8px; } .topGrid{ @@ -33,7 +21,6 @@ } .readout{ - background: transparent; text-align:center; padding: 10px 10px 0; } @@ -43,7 +30,7 @@ font-weight: 800; color: var(--muted); text-transform: uppercase; - font-size: 14px; + font-size: 13px; margin-top: 10px; } @@ -55,16 +42,16 @@ } .denary{ - font-size: 84px; - line-height: 1.0; - margin: 6px 0 16px; + font-size: 70px; /* smaller than before */ + line-height: 1; + margin: 6px 0 10px; } .binary{ - font-size: 62px; + font-size: 54px; /* smaller than before */ letter-spacing: .12em; - line-height: 1.0; - margin: 6px 0 18px; + line-height: 1; + margin: 6px 0 16px; } .controls{ @@ -81,7 +68,7 @@ color: #fff; padding: 12px 14px; border-radius: 12px; - font-weight: 800; + font-weight: 700; cursor: pointer; min-width: 160px; } @@ -92,7 +79,7 @@ border-top: 1px solid var(--line); } -/* Right panels */ +/* Right-side cards */ .panelCol{ display:flex; flex-direction:column; @@ -135,7 +122,7 @@ font-size: 14px; } -/* Switch (re-used for mode + bits) */ +/* Switch (reused for mode + bits) */ .switch{ position: relative; width: 56px; @@ -144,6 +131,7 @@ flex: 0 0 auto; } .switch input{ + position:absolute; opacity:0; width:0; height:0; @@ -176,7 +164,7 @@ background: var(--accent); } -/* Bit width controls */ +/* Bit-width control */ .bitWidthRow{ display:grid; grid-template-columns: 44px 1fr 44px; @@ -191,7 +179,7 @@ background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.14); color: #fff; - cursor:pointer; + cursor: pointer; font-weight: 900; font-size: 18px; } @@ -219,8 +207,8 @@ width: 86px; text-align:right; background: transparent; - border: none; - outline: none; + border:none; + outline:none; color: var(--accent); font-family: "DSEG7ClassicRegular", ui-monospace, monospace; font-size: 28px; @@ -231,63 +219,66 @@ margin:0; } -/* ✅ Bits grid: wraps every 8 bits, NO horizontal scroll bar */ -.bits{ - margin-top: 26px; - padding-top: 22px; - display:grid; - grid-template-columns: repeat(8, minmax(90px, 1fr)); +/* Bits: wrap every 8 (rows built in JS) */ +.bitsRows{ + margin-top: 22px; + display:flex; + flex-direction:column; gap: 18px; - align-items:end; - text-align:center; - - /* make absolutely sure we don't create a horizontal scrollbar */ - overflow-x: hidden; } -/* Bit tile */ +/* A row of up to 8 bits */ +.byteRow{ + display:flex; + justify-content:center; + gap: 18px; + flex-wrap:nowrap; +} + +/* A single bit */ .bit{ + width: 110px; display:flex; flex-direction:column; align-items:center; gap: 10px; - padding: 8px 4px; + padding: 6px 4px; } +/* Bulb emoji: bigger */ .bulb{ - width: 22px; - height: 22px; - border-radius: 50%; - background: rgba(255,255,255,.08); - border: 1px solid rgba(255,255,255,.12); - box-shadow: none; - margin-bottom: 4px; + font-size: 30px; + line-height: 1; + opacity: .20; + filter: grayscale(1); + transform: translateY(2px); } .bulb.on{ - background: #ffd86b; - border-color: rgba(255,216,107,.7); - box-shadow: 0 0 18px rgba(255,216,107,.6); + opacity: 1; + filter: grayscale(0); + text-shadow: 0 0 16px rgba(255,216,107,.65); } .bitVal{ font-family: "DSEG7ClassicRegular", ui-monospace, monospace; font-size: 30px; color: var(--text); - opacity: .95; - line-height: 1; + opacity: .92; min-height: 34px; } -/* Responsive */ +/* Responsiveness */ @media (max-width: 980px){ .topGrid{ grid-template-columns: 1fr; } - .denary{ font-size: 72px; } - .binary{ font-size: 52px; } - .bits{ grid-template-columns: repeat(4, minmax(90px, 1fr)); } + .denary{ font-size: 62px; } + .binary{ font-size: 48px; } + .bit{ width: 96px; } } @media (max-width: 520px){ - .denary{ font-size: 62px; } - .binary{ font-size: 44px; } + .denary{ font-size: 56px; } + .binary{ font-size: 42px; } .btn{ min-width: 140px; } + .byteRow{ gap: 12px; } + .bit{ width: 86px; } } diff --git a/public/styles/global.css b/public/styles/site.css similarity index 53% rename from public/styles/global.css rename to public/styles/site.css index 79b8405..5fdd4a9 100644 --- a/public/styles/global.css +++ b/public/styles/site.css @@ -4,9 +4,11 @@ --text: #e8e8ee; --muted: #a9acb8; --line: rgba(255,255,255,.12); + --accent: #33ff7a; + --accent-dim: rgba(51,255,122,.15); } -*{ box-sizing:border-box; } +*{ box-sizing: border-box; } body{ margin:0; @@ -15,61 +17,62 @@ body{ color: var(--text); } -.site-header{ - border-bottom: 1px solid var(--line); - background: rgba(0,0,0,.12); - backdrop-filter: blur(6px); +.page{ + min-height: calc(100vh - 120px); } -.site-header__inner{ +.siteHeader{ + position: sticky; + top: 0; + z-index: 10; + background: rgba(0,0,0,.15); + border-bottom: 1px solid var(--line); + backdrop-filter: blur(10px); +} + +.siteHeader__inner{ max-width: 1200px; margin: 0 auto; padding: 14px 20px; - display:flex; - align-items:center; - justify-content:space-between; - gap:16px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; } .brand{ - text-decoration:none; color: var(--text); - font-weight: 900; + text-decoration: none; + font-weight: 800; letter-spacing: .02em; } .nav{ - display:flex; + display: flex; gap: 14px; - flex-wrap:wrap; + flex-wrap: wrap; + justify-content: flex-end; } -.nav a{ +.nav__link{ color: var(--muted); - text-decoration:none; + text-decoration: none; font-weight: 700; font-size: 14px; } -.nav a:hover{ color: var(--text); } +.nav__link:hover{ color: var(--text); } -.site-main{ - min-height: calc(100vh - 120px); -} - -.site-footer{ +.siteFooter{ border-top: 1px solid var(--line); - margin-top: 28px; background: rgba(0,0,0,.10); } -.site-footer__inner{ +.siteFooter__inner{ max-width: 1200px; margin: 0 auto; padding: 18px 20px; - display:flex; - justify-content:space-between; - gap:12px; - flex-wrap:wrap; + color: var(--muted); + font-size: 12px; + display: grid; + gap: 6px; } - -.muted{ color: var(--muted); font-size: 13px; } diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 0000000..443552a --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,7 @@ + diff --git a/src/components/Header.astro b/src/components/Header.astro new file mode 100644 index 0000000..7746fba --- /dev/null +++ b/src/components/Header.astro @@ -0,0 +1,13 @@ + diff --git a/src/components/SiteFooter.astro b/src/components/SiteFooter.astro deleted file mode 100644 index 0948906..0000000 --- a/src/components/SiteFooter.astro +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/src/components/SiteHeader.astro b/src/components/SiteHeader.astro deleted file mode 100644 index eecd89e..0000000 --- a/src/components/SiteHeader.astro +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 7a7a8de..dfca60b 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -1,9 +1,10 @@ --- -import SiteHeader from "../components/SiteHeader.astro"; -import SiteFooter from "../components/SiteFooter.astro"; +import Header from "../components/Header.astro"; +import Footer from "../components/Footer.astro"; const { title = "Computing:Box" } = Astro.props; --- + @@ -11,16 +12,18 @@ const { title = "Computing:Box" } = Astro.props; {title} - - - - - + + -
+ + + + + +
+
- - +