diff --git a/src/assets/astro.svg b/src/assets/astro.svg deleted file mode 100644 index 8cf8fb0..0000000 --- a/src/assets/astro.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/components/Welcome.astro b/src/components/Welcome.astro deleted file mode 100644 index 52e0333..0000000 --- a/src/components/Welcome.astro +++ /dev/null @@ -1,210 +0,0 @@ ---- -import astroLogo from '../assets/astro.svg'; -import background from '../assets/background.svg'; ---- - -
- -
-
- Astro Homepage -

- To get started, open the
src/pages
directory in your project. -

- -
-
- - - -

What's New in Astro 5.0?

-

- From content layers to server islands, click to learn more about the new features and - improvements in Astro 5.0 -

-
-
- - diff --git a/src/components/simulators/HexSimulator.astro b/src/components/simulators/HexSimulator.astro deleted file mode 100644 index 4f212bb..0000000 --- a/src/components/simulators/HexSimulator.astro +++ /dev/null @@ -1,104 +0,0 @@ ---- -import "./hex/hex-simulator.css"; ---- - -
-
-
-
DENARY
-
0
- -
HEXADECIMAL
-
00
- -
BINARY
-
0000 0000
-
- -
- -
-
- - - - - - - - - -
-
Custom
- - - -
-
- -
- - -
-
-
- - -
diff --git a/src/components/simulators/hex/hex-simulator.css b/src/components/simulators/hex/hex-simulator.css deleted file mode 100644 index 8a15d61..0000000 --- a/src/components/simulators/hex/hex-simulator.css +++ /dev/null @@ -1,346 +0,0 @@ -/* ================= Fonts to match Binary ================= */ -/* Adjust paths to wherever you store fonts (commonly /public/fonts/...) */ -@font-face { - font-family: "DSEG7Classic"; - src: url("/fonts/DSEG7Classic-Regular.woff") format("woff"), - url("/fonts/DSEG7Classic-Regular.ttf") format("truetype"); - font-weight: 400; - font-style: normal; - font-display: swap; -} -@font-face { - font-family: "SevenSegment"; - src: url("/fonts/Seven-Segment.woff2") format("woff2"), - url("/fonts/Seven-Segment.woff") format("woff"); - font-weight: 400; - font-style: normal; - font-display: swap; -} - -.hex-sim { - min-height: 100vh; - background: #14151c; - color: #e7e8ee; - padding: 28px; -} - -.hex-font-number { font-family: "DSEG7Classic", ui-monospace, monospace; } -.hex-font-mono { font-family: "SevenSegment", ui-monospace, monospace; } - -.hex-main { max-width: 1200px; margin: 0 auto; width: 100%; padding-top: 40px; } - -.hex-readout { text-align: center; } -.hex-label { - font-family: "SevenSegment", ui-sans-serif, system-ui; - font-size: 12px; - letter-spacing: 2px; - opacity: 0.7; -} -.hex-mt { margin-top: 12px; } - -.hex-number { - font-family: "DSEG7Classic", ui-monospace, monospace; - font-size: 76px; - line-height: 1; - font-weight: 400; - color: #46ff8a; - text-shadow: 0 0 18px rgba(70,255,138,0.18); -} -.hex-number--small { font-size: 64px; } -.hex-number--tiny { font-size: 54px; letter-spacing: 6px; } - -.hex-divider { - margin: 26px auto 18px; - height: 1px; - width: min(760px, 90%); - background: rgba(255,255,255,0.10); -} - -/* ================= Main digit columns ================= */ -.hex-digits { - margin-top: 18px; - display: flex; - justify-content: center; - gap: 18px; - flex-wrap: wrap; -} - -.hex-digit-col { - width: 160px; - border-radius: 18px; - background: rgba(255,255,255,0.03); - border: 1px solid rgba(255,255,255,0.10); - padding: 12px; - display: grid; - gap: 10px; - justify-items: center; -} - -.hex-digit-controls { - width: 100%; - display: flex; - justify-content: center; - gap: 10px; -} - -.hex-digit-char { - font-size: 64px; - line-height: 1; - color: #46ff8a; - text-shadow: 0 0 18px rgba(70,255,138,0.18); -} - -.hex-digit-place { - font-family: "SevenSegment", ui-monospace, monospace; - opacity: 0.65; - font-size: 14px; - letter-spacing: 1px; -} - -/* ================= Bulbs (brightness changes) ================= */ -.hex-bulbs { - width: 100%; - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 10px; - align-items: end; -} - -.hex-bulb { - display: grid; - justify-items: center; - gap: 6px; - opacity: 0.35; - filter: grayscale(30%); - transition: opacity 160ms ease, filter 160ms ease; -} - -.hex-bulb .hex-bulb-cap { - width: 18px; - height: 18px; - border-radius: 999px; - background: rgba(255,255,255,0.22); - border: 1px solid rgba(255,255,255,0.14); -} - -.hex-bulb .hex-bulb-glow { - width: 18px; - height: 10px; - border-radius: 999px; - background: rgba(70,255,138,0.0); - box-shadow: 0 0 0 rgba(70,255,138,0.0); - transition: background 160ms ease, box-shadow 160ms ease; -} - -.hex-bulb .hex-bulb-label { - font-family: "SevenSegment", ui-monospace, monospace; - font-size: 12px; - opacity: 0.8; -} - -.hex-bulb.is-on { - opacity: 1; - filter: none; -} -.hex-bulb.is-on .hex-bulb-cap { - background: rgba(255,255,255,0.35); -} -.hex-bulb.is-on .hex-bulb-glow { - background: rgba(70,255,138,0.25); - box-shadow: 0 0 18px rgba(70,255,138,0.35); -} - -/* ================= Buttons (toolbox style reused everywhere) ================= */ -.hex-btn { - padding: 10px 12px; - border-radius: 14px; - border: 1px solid rgba(255,255,255,0.14); - background: rgba(255,255,255,0.06); - color: #e7e8ee; - font-weight: 800; - cursor: pointer; - font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; -} -.hex-btn:hover { background: rgba(255,255,255,0.10); } - -.hex-btn--square { - width: 48px; - height: 48px; - padding: 0; - display: grid; - place-items: center; - font-size: 18px; -} - -.hex-btn--wide { width: 100%; } - -.hex-btn--green { - background: rgba(46, 200, 120, 0.18); - border-color: rgba(46,200,120,0.35); -} -.hex-btn--green:hover { background: rgba(46, 200, 120, 0.26); } - -.hex-btn--green2 { - background: rgba(46, 200, 120, 0.18); - border-color: rgba(46,200,120,0.35); -} - -.hex-btn--red { - background: rgba(220, 60, 70, 0.18); - border-color: rgba(220,60,70,0.35); -} - -/* Random = green pulse while running */ -.hex-btn--random.is-running { - border-color: rgba(80, 255, 160, 0.55); - background: rgba(46, 200, 120, 0.22); - box-shadow: 0 0 18px rgba(80, 255, 160, 0.35); - animation: hexPulseGreen 900ms ease-in-out infinite; -} -@keyframes hexPulseGreen { - 0%, 100% { box-shadow: 0 0 14px rgba(80, 255, 160, 0.25); } - 50% { box-shadow: 0 0 26px rgba(80, 255, 160, 0.45); } -} - -/* Reset = red background + pulse on hover */ -.hex-btn--reset:hover { - background: rgba(220, 60, 70, 0.28); - border-color: rgba(255, 80, 90, 0.55); - animation: hexPulseRed 900ms ease-in-out infinite; -} -@keyframes hexPulseRed { - 0%, 100% { box-shadow: 0 0 12px rgba(255, 80, 90, 0.20); } - 50% { box-shadow: 0 0 22px rgba(255, 80, 90, 0.38); } -} - -/* ================= Toolbox button + panel (slide) ================= */ -.hex-toolbox-btn { - position: fixed; - top: 88px; - right: 28px; - z-index: 30; - display: inline-flex; - align-items: center; - gap: 10px; - padding: 12px 16px; - border-radius: 14px; - border: 1px solid rgba(255,255,255,0.14); - background: rgba(255,255,255,0.06); - color: #e7e8ee; - font-weight: 800; - letter-spacing: 1px; - cursor: pointer; -} - -.hex-toolbox-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 18px; - height: 18px; - color: #ff4fa6; - filter: drop-shadow(0 0 10px rgba(255,79,166,0.35)); -} - -.hex-toolbox { - position: fixed; - top: 140px; - right: 28px; - width: 340px; - display: grid; - gap: 14px; - z-index: 25; - - transform: translateX(0); - opacity: 1; - transition: transform 220ms ease, opacity 220ms ease; -} -.hex-toolbox:not(.is-open) { - transform: translateX(380px); - opacity: 0; - pointer-events: none; -} - -.hex-panel { - border-radius: 16px; - background: rgba(255,255,255,0.04); - border: 1px solid rgba(255,255,255,0.10); - padding: 14px; -} -.hex-panel-title { - font-family: "SevenSegment", ui-sans-serif, system-ui; - font-size: 12px; - letter-spacing: 2px; - opacity: 0.7; - margin-bottom: 10px; -} - -.hex-setting-title { font-weight: 900; opacity: 0.9; margin-bottom: 10px; } - -.hex-width { - display: grid; - grid-template-columns: 48px 1fr 48px; - gap: 10px; - align-items: center; -} -.hex-width-readout { - border-radius: 14px; - background: rgba(0,0,0,0.22); - border: 1px solid rgba(255,255,255,0.10); - padding: 10px 12px; - display: flex; - justify-content: space-between; - align-items: baseline; -} -.hex-width-label { - font-family: "SevenSegment", ui-sans-serif, system-ui; - opacity: 0.7; - font-weight: 800; - letter-spacing: 1px; - font-size: 12px; -} -.hex-width-number { font-size: 30px; font-weight: 900; color: #46ff8a; } - -.hex-hint { margin-top: 8px; opacity: 0.65; font-size: 12px; font-family: "SevenSegment", ui-sans-serif, system-ui; } - -.hex-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } -.hex-mt-sm { margin-top: 10px; } - -.hex-tools-top { display: flex; gap: 10px; justify-content: center; margin-bottom: 10px; } -.hex-tiny-note { margin-top: 8px; font-size: 11px; opacity: 0.6; letter-spacing: 1px; font-family: "SevenSegment", ui-sans-serif, system-ui; } - -/* ================= Dialog ================= */ -.hex-dialog { border: none; padding: 0; background: transparent; } -.hex-dialog::backdrop { background: rgba(0,0,0,0.55); } - -.hex-dialog-card { - width: min(560px, 92vw); - border-radius: 18px; - background: #1a1b24; - border: 1px solid rgba(255,255,255,0.12); - padding: 16px; - color: #e7e8ee; -} - -.hex-dialog-title { font-weight: 900; letter-spacing: 1px; margin-bottom: 10px; font-family: "SevenSegment", ui-sans-serif, system-ui; } - -.hex-dialog-input { - width: 100%; - padding: 12px 12px; - border-radius: 14px; - border: 1px solid rgba(255,255,255,0.14); - background: rgba(0,0,0,0.25); - color: #e7e8ee; - font-size: 18px; -} - -.hex-dialog-hint { margin-top: 10px; opacity: 0.7; font-size: 13px; font-family: "SevenSegment", ui-sans-serif, system-ui; } -.hex-dialog-error { margin-top: 8px; font-size: 13px; color: #ff6b6b; min-height: 18px; font-family: "SevenSegment", ui-sans-serif, system-ui; } -.hex-dialog-actions { margin-top: 14px; display: flex; gap: 10px; justify-content: flex-end; } - -@media (max-width: 900px) { - .hex-toolbox { width: min(360px, 92vw); right: 16px; } - .hex-toolbox-btn { right: 16px; } - .hex-number { font-size: 60px; } - .hex-number--tiny { font-size: 40px; letter-spacing: 4px; } -} diff --git a/src/components/simulators/hex/hex-simulator.ts b/src/components/simulators/hex/hex-simulator.ts deleted file mode 100644 index 08bc8aa..0000000 --- a/src/components/simulators/hex/hex-simulator.ts +++ /dev/null @@ -1,232 +0,0 @@ -type DialogMode = "hex" | "den" | "bin"; - -const root = document.querySelector("[data-hex-sim]"); -if (!root) throw new Error("Hex simulator root not found"); - -const outDen = root.querySelector('[data-out="denary"]')!; -const outHex = root.querySelector('[data-out="hex"]')!; -const outBin = root.querySelector('[data-out="bin"]')!; -const outDigitsRow = root.querySelector('[data-out="digitsRow"]')!; - -const toolbox = root.querySelector('[data-out="toolbox"]')!; -const toolboxBtn = root.querySelector('[data-action="toggleToolbox"]')!; -const digitsCount = root.querySelector('[data-out="digitsCount"]')!; -const bitsHint = root.querySelector('[data-out="bitsHint"]')!; -const randomBtn = root.querySelector("[data-random]")!; - -const dialog = root.querySelector('[data-out="dialog"]')!; -const dialogTitle = root.querySelector('[data-out="dialogTitle"]')!; -const dialogInput = root.querySelector('[data-out="dialogInput"]')!; -const dialogHint = root.querySelector('[data-out="dialogHint"]')!; -const dialogError = root.querySelector('[data-out="dialogError"]')!; - -let digits = 2; // 1..8 -let value = 0; // unsigned denary -let randomTimer: number | null = null; -let dialogMode: DialogMode | null = null; - -const clamp = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n)); -const maxForDigits = (d: number) => (16 ** d) - 1; - -const padHex = (n: number, d: number) => n.toString(16).toUpperCase().padStart(d, "0"); -const padBin = (n: number, b: number) => n.toString(2).padStart(b, "0"); -const groupBin = (b: string) => b.replace(/(.{4})/g, "$1 ").trim(); - -function stopRandom(): void { - if (randomTimer !== null) window.clearInterval(randomTimer); - randomTimer = null; - randomBtn.classList.remove("is-running"); -} - -function startRandom(): void { - stopRandom(); - const max = maxForDigits(digits); - const start = Date.now(); - - randomBtn.classList.add("is-running"); - - randomTimer = window.setInterval(() => { - value = Math.floor(Math.random() * (max + 1)); - render(); - if (Date.now() - start > 1600) stopRandom(); - }, 90); -} - -function render(): void { - const bits = digits * 4; - - digitsCount.textContent = String(digits); - bitsHint.textContent = `= ${bits} bits`; - - outDen.textContent = String(value); - outHex.textContent = padHex(value, digits); - outBin.textContent = groupBin(padBin(value, bits)); - - renderDigitsRow(); -} - -function renderDigitsRow(): void { - const hex = padHex(value, digits); - outDigitsRow.innerHTML = ""; - - for (let i = 0; i < digits; i++) { - const pow = digits - 1 - i; - const placeValue = 16 ** pow; - - const digitChar = hex[i]; - const digitVal = parseInt(digitChar, 16); - const nibbleBits = [(digitVal >> 3) & 1, (digitVal >> 2) & 1, (digitVal >> 1) & 1, digitVal & 1]; // 8 4 2 1 - - const col = document.createElement("div"); - col.className = "hex-digit-col"; - col.innerHTML = ` -
- - -
- -
${digitChar}
- - -
- ${[8,4,2,1].map((w, idx) => { - const on = nibbleBits[idx] === 1; - return ` -
-
-
-
${w}
-
- `; - }).join("")} -
- -
${placeValue}
- `; - outDigitsRow.appendChild(col); - } -} - -function openDialog(mode: DialogMode): void { - stopRandom(); - dialogMode = mode; - - dialogError.textContent = ""; - dialogInput.value = ""; - - if (mode === "hex") { - dialogTitle.textContent = "Custom Hexadecimal"; - dialogHint.textContent = `Enter 1–${digits} hex digit(s) (0–9, A–F).`; - dialogInput.placeholder = "A1"; - dialogInput.inputMode = "text"; - } else if (mode === "den") { - dialogTitle.textContent = "Custom Denary"; - dialogHint.textContent = `Enter a whole number from 0 to ${maxForDigits(digits)}.`; - dialogInput.placeholder = "42"; - dialogInput.inputMode = "numeric"; - } else { - dialogTitle.textContent = "Custom Binary"; - dialogHint.textContent = `Enter up to ${digits * 4} bit(s) using 0 and 1.`; - dialogInput.placeholder = "00101010"; - dialogInput.inputMode = "text"; - } - - dialog.showModal(); - window.setTimeout(() => dialogInput.focus(), 0); -} - -function closeDialog(): void { - dialogMode = null; - dialogError.textContent = ""; - if (dialog.open) dialog.close(); -} - -function applyDialog(): void { - const raw = (dialogInput.value || "").trim(); - if (!dialogMode) return closeDialog(); - if (raw.length === 0) return closeDialog(); - - const max = maxForDigits(digits); - const bits = digits * 4; - - if (dialogMode === "hex") { - const v = raw.toUpperCase(); - if (!/^[0-9A-F]+$/.test(v)) { dialogError.textContent = "Hex must use 0–9 and A–F only."; return; } - if (v.length > digits) { dialogError.textContent = `Max length is ${digits} hex digit(s).`; return; } - value = clamp(parseInt(v, 16), 0, max); - render(); - return closeDialog(); - } - - if (dialogMode === "den") { - if (!/^\d+$/.test(raw)) { dialogError.textContent = "Denary must be whole numbers only."; return; } - const n = Number(raw); - if (!Number.isFinite(n)) { dialogError.textContent = "Invalid number."; return; } - value = clamp(n, 0, max); - render(); - return closeDialog(); - } - - // bin - if (!/^[01]+$/.test(raw)) { dialogError.textContent = "Binary must use 0 and 1 only."; return; } - if (raw.length > bits) { dialogError.textContent = `Max length is ${bits} bit(s).`; return; } - value = clamp(parseInt(raw, 2), 0, max); - render(); - return closeDialog(); -} - -function applyDigitDelta(i: number, delta: number): void { - stopRandom(); - const hexArr = padHex(value, digits).split(""); - let v = parseInt(hexArr[i], 16); - v = (v + delta) % 16; - if (v < 0) v += 16; - hexArr[i] = v.toString(16).toUpperCase(); - value = clamp(parseInt(hexArr.join(""), 16), 0, maxForDigits(digits)); - render(); -} - -// dialog cancel / backdrop -dialog.addEventListener("cancel", (e) => { e.preventDefault(); closeDialog(); }); -dialog.addEventListener("click", (e) => { - const card = dialog.querySelector(".hex-dialog-card"); - if (card && !card.contains(e.target as Node)) closeDialog(); -}); -dialogInput.addEventListener("keydown", (e) => { - if (e.key === "Enter") applyDialog(); - if (e.key === "Escape") closeDialog(); -}); - -// main click handler -root.addEventListener("click", (e) => { - const btn = (e.target as HTMLElement).closest("[data-action]"); - if (!btn) return; - const action = btn.getAttribute("data-action")!; - - if (action === "toggleToolbox") { - toolbox.classList.toggle("is-open"); - toolboxBtn.setAttribute("aria-expanded", toolbox.classList.contains("is-open") ? "true" : "false"); - return; - } - - if (action === "digitsMinus") { digits = clamp(digits - 1, 1, 8); value = clamp(value, 0, maxForDigits(digits)); return render(); } - if (action === "digitsPlus") { digits = clamp(digits + 1, 1, 8); value = clamp(value, 0, maxForDigits(digits)); return render(); } - - if (action === "increment") { stopRandom(); value = clamp(value + 1, 0, maxForDigits(digits)); return render(); } - if (action === "decrement") { stopRandom(); value = clamp(value - 1, 0, maxForDigits(digits)); return render(); } - - if (action === "reset") { stopRandom(); value = 0; return render(); } - if (action === "random") { return startRandom(); } - - if (action === "customHex") return openDialog("hex"); - if (action === "customDenary") return openDialog("den"); - if (action === "customBinary") return openDialog("bin"); - - if (action === "dialogCancel") return closeDialog(); - if (action === "dialogApply") return applyDialog(); - - if (action === "digitUp") return applyDigitDelta(Number(btn.getAttribute("data-i")), +1); - if (action === "digitDown") return applyDigitDelta(Number(btn.getAttribute("data-i")), -1); -}); - -render(); diff --git a/src/components/site-footer.astro b/src/components/site-footer.astro deleted file mode 100644 index bb1625e..0000000 --- a/src/components/site-footer.astro +++ /dev/null @@ -1,8 +0,0 @@ -
- -
diff --git a/src/components/site-header.astro b/src/components/site-header.astro deleted file mode 100644 index 3ce3958..0000000 --- a/src/components/site-header.astro +++ /dev/null @@ -1,25 +0,0 @@ ---- -const nav = [ - { href: "/about", label: "About" }, - { href: "/binary", label: "Binary" }, - { href: "/hexadecimal", label: "Hexadecimal" }, - { href: "/hex-colours", label: "Hex Colours" }, - { href: "/logic-gates", label: "Logic Gates" }, -]; ---- - diff --git a/src/components/tools/BinarySimulator.astro b/src/components/tools/BinarySimulator.astro deleted file mode 100644 index 43d5ce1..0000000 --- a/src/components/tools/BinarySimulator.astro +++ /dev/null @@ -1,381 +0,0 @@ ---- -const { mode = "unsigned", defaultBits = 8 } = Astro.props; - -// For unsigned: min 1 bit, max 16 bits (tweak if you want) -const minBits = 4; -const maxBits = 16; -const initialBits = Math.min(Math.max(defaultBits, minBits), maxBits); ---- - -
-
-
- - -
- -
-
DENARY
-
0
- -
BINARY
-
00000000
-
- -
- - - - - -
-
Bits
-
- - {initialBits} - -
-
-
-
- -
-
- - - - diff --git a/src/pages/index.astro b/src/pages/index.astro deleted file mode 100644 index c04f360..0000000 --- a/src/pages/index.astro +++ /dev/null @@ -1,11 +0,0 @@ ---- -import Welcome from '../components/Welcome.astro'; -import Layout from '../layouts/Layout.astro'; - -// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build -// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh. ---- - - - - diff --git a/src/pages/logic-gates.astro b/src/pages/logic-gates.astro index 7eb4c54..dd4dbad 100644 --- a/src/pages/logic-gates.astro +++ b/src/pages/logic-gates.astro @@ -4,41 +4,31 @@ import "../styles/logic-gates.css"; --- -
+
-
- -
-
-
Interactive Simulator
-
LOGIC GATES
-
-
- Drag items from the toolbox to the board. Drag from output ports to input ports to wire. Click a wire or node and press Delete to remove it. -
+
+
Interactive Logic Circuit Builder
+
+ Drag items from the toolbox to the board. Drag from output ports to input ports to wire. Click a wire or node and press Delete to remove it.
+
-
- -
- -
- -
- -