diff --git a/public/images/BitBoxLogo.png b/public/images/BitBoxLogo.png new file mode 100644 index 0000000..53ba610 Binary files /dev/null and b/public/images/BitBoxLogo.png differ diff --git a/public/images/Educational_Impact.webp b/public/images/Educational_Impact.webp new file mode 100644 index 0000000..04aa426 Binary files /dev/null and b/public/images/Educational_Impact.webp differ diff --git a/public/images/computingbox-concept-illustration.webp b/public/images/computingbox-concept-illustration.webp new file mode 100644 index 0000000..3dd1e84 Binary files /dev/null and b/public/images/computingbox-concept-illustration.webp differ diff --git a/src/pages/about.astro b/src/pages/about.astro index 80122a7..0496060 100644 --- a/src/pages/about.astro +++ b/src/pages/about.astro @@ -2,36 +2,54 @@ import BaseLayout from "../layouts/BaseLayout.astro"; --- - -
-
-

The New Computing:Box Experience

-

- The platform has been rebuilt from the ground up to provide a deeper, more professional simulation environment. - We've introduced several key simulators and interface upgrades to support the modern Computing classroom: -

- -
+ +
+ +
+ Computing:Box Logo +

The New Computing:Box Experience

+
-
    +
    +

    + Rebuilt from the ground up to provide a deeper, more professional simulation environment for the UK Computing Curriculum. +

    +
    +
    • New User Interface (Responsive)
    • Two's Complement Simulator
    • -
    • Extended Binary Simulator (Custom bit sizes)
    • -
    • Unified Binary Simulator (Unsigned & Two's Complement)
    • +
    • Extended Binary Simulator (Custom sizes)
    • +
    • Unified Binary Simulator
    • Extended Hexadecimal Simulator
    • -
    • Unified Hexadecimal Simulator (GCSE & A Level)
    • -
    • Enhanced Gate Simulator (Truth Table Creator)
    • +
    • Unified Hexadecimal Simulator
    • +
    • Enhanced Gate Simulator (Truth Tables)
    • Compound Gate Simulator
    • Computer Components Simulator
    -
    -

    Educational Impact

    -

    - Computing:Box is designed to help students learn computing concepts step by step, using clear visuals and simple interactions. - By changing values and seeing results straight away, students can build understanding at their own pace. -

    +
    +
    +

    From Bit:Box to Computing:Box

    +

    Computing:Box began as Bit:Box, a digital version of a physical Binary Light Box created by Mr Davis. The original device used lightbulbs to show how binary numbers are built.

    +
    + Bit:Box Logo +
    + +
    + Simulator Illustration +
    +

    Learning by Doing

    +

    Students can interact with simulations, change values, and see results instantly. This helps them understand how concepts work rather than memorising rules.

    +
    +
    + +
    +
    +

    Educational Impact

    +

    Designed for use in lessons, independent study, and revision, helping students build confidence as they explore how computers work.

    +
    + Classroom Impact
\ No newline at end of file diff --git a/src/scripts/pcBuilder.js b/src/scripts/pcBuilder.js index b23e2b2..087058a 100644 --- a/src/scripts/pcBuilder.js +++ b/src/scripts/pcBuilder.js @@ -4,13 +4,17 @@ (() => { const workspace = document.getElementById("workspace"); const viewport = document.getElementById("viewport"); - const wireLayer = document.getElementById("wireLayer"); + const internalLayer = document.getElementById("wireLayerInternal"); + const externalLayer = document.getElementById("wireLayerExternal"); const specsContainer = document.getElementById("buildSpecsContainer"); const toolboxGrid = document.getElementById("toolboxGrid"); const btnClearBoard = document.getElementById("btnClearBoard"); const toolboxToggle = document.getElementById("toolboxToggle"); const pcPage = document.getElementById("pcPage"); + // Parts defined as "Outside the Chassis" + const EXTERNAL_TYPES = ['MONITOR', 'KEYBOARD', 'MOUSE', 'WEBCAM', 'SPEAKER', 'MIC', 'PRINTER']; + /* --- Extensive PC Component Library --- */ const PC_PARTS = { 'CASE': { @@ -98,17 +102,11 @@ panY = mouseY - (mouseY - panY) * (newZoom / zoom); zoom = newZoom; updateViewport(); } - function getPortCoords(nodeId, portDataAttr) { - const node = nodes[nodeId]; - if (!node || !node.el) return {x:0, y:0}; - const portEl = node.el.querySelector(`[data-port="${portDataAttr}"]`); - if (!portEl) return {x:0, y:0}; - const wsRect = workspace.getBoundingClientRect(); - const portRect = portEl.getBoundingClientRect(); - return { - x: (portRect.left - wsRect.left - panX + portRect.width / 2) / zoom, - y: (portRect.top - wsRect.top - panY + portRect.height / 2) / zoom - }; + function getPortCoords(nodeId, portId) { + const n = nodes[nodeId]; + const pEl = n.el.querySelector(`[data-port="${portId}"]`); + const r = pEl.getBoundingClientRect(), w = workspace.getBoundingClientRect(); + return { x: (r.left - w.left - panX) / zoom, y: (r.top - w.top - panY) / zoom }; } function drawBezier(x1, y1, x2, y2) { const cpDist = Math.abs(x2 - x1) * 0.6 + 20; @@ -117,17 +115,22 @@ /* --- Rendering --- */ function renderWires() { - let svgHTML = ''; - connections.forEach(conn => { - const from = getPortCoords(conn.fromNode, conn.fromPort); - const to = getPortCoords(conn.toNode, conn.toPort); - const isSelected = conn.id === selectedWireId; - svgHTML += ``; + let intHtml = '', extHtml = ''; + connections.forEach(c => { + const fromNode = nodes[c.fN]; + const toNode = nodes[c.tN]; + if (!fromNode || !toNode) return; + + const p1 = getPortCoords(c.fN, c.fP); + const p2 = getPortCoords(c.tN, c.tP); + const path = ``; + + // Determine if cable is Internal (under panel) or External (on top) + (EXTERNAL_TYPES.includes(fromNode.type) || EXTERNAL_TYPES.includes(toNode.type)) ? extHtml += path : intHtml += path; }); - if (wiringStart && tempWirePath) { - svgHTML += ``; - } - wireLayer.innerHTML = svgHTML; + internalLayer.innerHTML = intHtml; + externalLayer.innerHTML = extHtml; + evaluateBuild(); } function updateNodePositions() { @@ -145,67 +148,10 @@ /* --- Seven-Segment Diagnostics Engine --- */ function evaluateBuild() { - if(!specsContainer) return; - - let hasCase = false, hasMB = false, hasCPU = false, hasCooler = false, hasRAM = false, hasPSU = false; - let hasStorage = false, hasGPU = false; - let mbPwr = false, gpuPwr = false; - let usbCount = 0, dispConn = false, audConn = false; - - let caseNode = Object.values(nodes).find(n => n.type === 'CASE'); - let mbNode = Object.values(nodes).find(n => n.type === 'MB'); - - if (caseNode) { - hasCase = true; - if (caseNode.slots['MB1']) hasMB = true; - if (caseNode.slots['PSU1']) hasPSU = true; - if (caseNode.slots['HDD1'] || caseNode.slots['HDD2'] || caseNode.slots['SATA_SSD1'] || caseNode.slots['SATA_SSD2']) hasStorage = true; - } else if (mbNode) { - hasMB = true; // Motherboard exists outside case - } - - if (mbNode) { - if (mbNode.slots['CPU1']) hasCPU = true; - if (mbNode.slots['COOLER1']) hasCooler = true; - if (mbNode.slots['RAM1'] || mbNode.slots['RAM2'] || mbNode.slots['RAM3'] || mbNode.slots['RAM4']) hasRAM = true; - if (mbNode.slots['PCIE1'] || mbNode.slots['PCIE2']) hasGPU = true; - if (mbNode.slots['M2_1'] || mbNode.slots['M2_2']) hasStorage = true; - } - - // Check Cables - connections.forEach(c => { - let n1 = nodes[c.fromNode], n2 = nodes[c.toNode]; - if(!n1 || !n2) return; - let types = [n1.type, n2.type]; - - if(types.includes('MB') && types.includes('PSU')) mbPwr = true; - if(types.includes('GPU') && types.includes('PSU')) gpuPwr = true; - if(types.includes('MB') && ['KEYBOARD','MOUSE','WEBCAM','MIC','PRINTER'].some(t => types.includes(t))) usbCount++; - if(types.includes('MB') && types.includes('SPEAKER')) audConn = true; - if((types.includes('MB') || types.includes('GPU')) && types.includes('MONITOR')) dispConn = true; - }); - - const isBootable = (hasMB && hasCPU && hasCooler && hasRAM && hasPSU && hasStorage && mbPwr && (hasGPU ? gpuPwr : true) && dispConn); - - specsContainer.innerHTML = ` -
Core System
-
CHASSIS${hasCase ? 'OK' : 'ERR'}
-
MOTHERBOARD${hasMB ? 'OK' : 'ERR'}
-
CPU${hasCPU ? 'OK' : 'ERR'}
-
COOLING${hasCooler ? 'OK' : 'ERR'}
-
MEMORY${hasRAM ? 'OK' : 'ERR'}
-
POWER SPLY${hasPSU ? 'OK' : 'ERR'}
-
Connections
-
MB POWER${mbPwr ? 'OK' : 'ERR'}
-
STORAGE${hasStorage ? 'OK' : 'ERR'}
-
GPU POWER${!hasGPU ? 'N/A' : (gpuPwr ? 'OK' : 'ERR')}
-
DISPLAY${dispConn ? 'OK' : 'ERR'}
-
USB DEVS${usbCount}
-
-
- ${isBootable ? 'BOOTING...' : 'HALTED'} -
- `; + const hasMB = Object.values(nodes).some(n => n.type === 'MB' && n.snappedTo); + const hasCPU = Object.values(nodes).some(n => n.type === 'CPU' && n.snappedTo); + const boot = hasMB && hasCPU && connections.some(c => nodes[c.fN].type === 'MONITOR' || nodes[c.tN].type === 'MONITOR'); + workspace.classList.toggle('system-running', boot); } /* --- Node Creation & Snapping --- */ @@ -250,16 +196,12 @@ evaluateBuild(); } - // Recursive movement to handle nested snaps (MB inside CASE inside ...) +/* --- Movement & Snap Logic (Restored from your Verified Working Script) --- */ function moveNodeRecursive(nodeId, dx, dy) { - const n = nodes[nodeId]; - if(!n) return; + const n = nodes[nodeId]; if(!n) return; n.x += dx; n.y += dy; - if(n.slots) { - Object.keys(n.slots).forEach(k => { - if(typeof n.slots[k] === 'string') moveNodeRecursive(n.slots[k], dx, dy); - }); - } + if(n.slots) { Object.keys(n.slots).forEach(k => { if(typeof n.slots[k] === 'string') moveNodeRecursive(n.slots[k], dx, dy); }); } + if(n.el) { n.el.style.left = n.x + 'px'; n.el.style.top = n.y + 'px'; } } /* --- Inspect Mode --- */ diff --git a/src/styles/global.css b/src/styles/global.css index ec816f0..c02a5bb 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -23,58 +23,75 @@ --text: #e8e8ee; --muted: #a9acb8; --line: rgba(255,255,255,.10); - - --ui-font: "Inter", system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; - --num-font: "DSEG7Classic", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; + --accent: #28f07a; + --ui-font: "Inter", system-ui, -apple-system, sans-serif; --bit-font: "SevenSegment", monospace; } * { box-sizing: border-box; } -html, body { height: 100%; } -body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--ui-font); display: flex; flex-direction: column; } +html, body { height: 100%; margin: 0; } +body { background: var(--bg); color: var(--text); font-family: var(--ui-font); display: flex; flex-direction: column; } -/* --- BASE LAYOUT --- */ -.siteNav { position: sticky; top: 0; z-index: 50; height: var(--nav-h); background: rgba(0,0,0,.10); border-bottom: 1px solid var(--line); backdrop-filter: blur(8px); } -.navInner { height: 100%; max-width: 1400px; margin: 0 auto; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; gap: 24px; } +/* --- SITE NAVIGATION (Fixed Height) --- */ +.siteNav { + position: sticky; + top: 0; + z-index: 500; + height: var(--nav-h); + background: rgba(45, 44, 56, 0.95); + border-bottom: 1px solid var(--line); + backdrop-filter: blur(8px); +} +.navInner { + height: 100%; + max-width: 1400px; + margin: 0 auto; + padding: 0 20px; + display: flex; + align-items: center; + justify-content: space-between; +} .brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--text); } -.brandLogo { width: 2.5em; height: 2.5em; image-rendering: pixelated; } +.brandLogo { width: 32px; height: 32px; } .brandName { letter-spacing: .12em; font-weight: 900; font-size: 18px; } -.navLinks { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; } -.navLinks a { color: var(--muted); text-decoration: none; font-weight: 800; letter-spacing: .12em; font-size: 16px; } -.navLinks a:hover, .navLinks a.active { color: #e8e8ee; } -.pageWrap { flex: 1; max-width: 1400px; margin: 0 auto; padding: 0 20px 40px; width: 100%; display: flex; flex-direction: column; } -.siteFooter { border-top: 1px solid var(--line); background: rgba(0,0,0,.08); } -.footerInner { max-width: 1400px; margin: 0 auto; padding: 18px 20px; color: var(--muted); font-size: 12px; letter-spacing: .08em; display: flex; flex-direction: column; gap: 6px; } +.navLinks { display: flex; gap: 24px; } +.navLinks a { + color: var(--muted); + text-decoration: none; + font-weight: 800; + font-size: 14px; + + transition: color 0.2s; +} -/* --- SHARED UI COMPONENTS (Used by ALL Simulators) --- */ -.divider { height: 1px; background: rgba(255,255,255,.08); margin: 16px 0 16px; } +/* Active Link Fix */ +.navLinks a.active, +.navLinks a:hover { + color: #ffffff !important; +} -.bulb { width: 44px; height: 44px; color: rgba(255,255,255,.15); margin-bottom: 8px; flex-shrink: 0; transition: 0.2s ease; background: transparent; display: flex; align-items: center; justify-content: center; } -.bulb svg { width: 100%; height: 100%; display: block; } -.bulb.on { color: #ffd86b !important; filter: drop-shadow(0 0 14px rgba(255, 216, 107, 1)) !important; } -.bulb.on svg { fill: #ffd86b !important; } +/* --- LAYOUT --- */ +.pageWrap { flex: 1; width: 100%; max-width: 1400px; margin: 0 auto; padding: 40px 20px; display: flex; flex-direction: column; } -.switch { position: relative; width: 56px; height: 28px; display: inline-block; } -.switch input { display: none; } -.slider { position: absolute; inset: 0; background: rgba(255,255,255,.14); border: 1px solid rgba(255,255,255,.14); border-radius: 999px; transition: .2s ease; } -.slider::before { content: ""; position: absolute; width: 22px; height: 22px; left: 3px; top: 2px; background: rgba(255,255,255,.92); border-radius: 999px; transition: .2s ease; pointer-events: none; } -.switch input:checked + .slider { background: rgba(40,240,122,.25); border-color: rgba(40,240,122,.30); } -.switch input:checked + .slider::before { transform: translateX(28px); } +/* --- FOOTER (Centered Links at Top) --- */ +.siteFooter { background: rgba(0,0,0,.08); border-top: 1px solid var(--line); padding: 40px 0; } +.footerInner { + max-width: 1400px; + margin: 0 auto; + padding: 0 20px; + color: var(--muted); + font-size: 13px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 12px; +} +.footerLegal { margin-bottom: 8px; } +.footerLegal a { color: var(--muted); text-decoration: underline; font-weight: 700; margin: 0 10px; } +.footerLegal a:hover { color: #fff; } -.card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.10); border-radius: 16px; padding: 16px; backdrop-filter: blur(10px); } -.cardTitle { font-family: var(--bit-font); font-weight: 900; letter-spacing: .14em; text-transform: uppercase; font-size: 18px; color: rgba(232,232,238,.9); margin-bottom: 12px; } -.hint { font-family: var(--bit-font); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: rgba(232,232,238,.55); margin-top: 10px; line-height: 1.35; } - -.btn { border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.06); color: rgba(232,232,238,.92); border-radius: 12px; padding: 10px 12px; font-family: var(--bit-font); font-size: 14px; letter-spacing: .12em; text-transform: uppercase; font-weight: 900; cursor: pointer; } -.btn:hover { border-color: rgba(255,255,255,.22); } -.btnAccent { background: rgba(40,240,122,.12); border-color: rgba(40,240,122,.22); } -.btnAccent:hover { border-color: rgba(40,240,122,.35); } -.btnHalf { width: calc(50% - 6px); } -.btnWide { width: 100%; } -.btnReset { color: rgba(232,232,238,.95); } -.btnReset:hover { background: rgba(255,80,80,.18); border-color: rgba(255,80,80,.35); } - -.toolboxToggle { position: fixed; top: var(--toolbox-toggle-top, calc(var(--nav-h) + 16px)); right: 22px; z-index: 90; display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 12px; border: 1px solid rgba(255,255,255,.12); background: rgba(0,0,0,.15); backdrop-filter: blur(8px); color: rgba(232,232,238,.95); font-family: var(--bit-font); font-weight: 800; font-size: 16px; letter-spacing: .12em; text-transform: uppercase; cursor: pointer; } -.toolboxIcon { font-size: 20px; filter: drop-shadow(0 0 8px rgba(255,105,180,.35)); } -.toolboxToggle:hover { border-color: rgba(255,255,255,.22); } \ No newline at end of file +/* Shared UI Components */ +.card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.10); border-radius: 16px; padding: 24px; backdrop-filter: blur(10px); } +.divider { height: 1px; background: rgba(255,255,255,.08); margin: 16px 0 16px; } \ No newline at end of file