feat(alpha): introduce Computing:Box prototype alpha branding and assets

- Update README to reflect Computing:Box as the evolution of Bit:Box and CS:Box
- Replace CS:Box logo references with Computing:Box branding
- Add initial Bootstrap Studio design file for prototype layout
- Include legacy CS:Box logo assets for reference and transition
- Establish baseline assets for Prototype Alpha phase

Signed-off-by: Alexander Lyall <alex@adcm.uk>
This commit is contained in:
2025-12-14 17:06:57 +00:00
parent 50829688e3
commit 58202ca853
19 changed files with 288 additions and 256 deletions

View File

@@ -45,12 +45,10 @@ All commands are run from the root of the project, from a terminal:
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
# This project has evolved to become Computing:Box Available at [www.computingbox.co.uk](https://www.computingbox.co.uk) and [www.computingbox.co.uk](https://www.computingbox.co.uk) # Computing:Box
An evolution of Bit:Box & CS:Box to incorporate different elements of the UK Computing Curriculum
# CS:Box ![Computing:Box Logo](/assets/img/ComputingBox-Logo.png "Computing:Box Logo")
An evolution of Bit:Box to incorporate different elements of the UK Computing Curriculum
![CS:Box Logo](/assets/img/CSBox-Logo.png "CS:Box Logo")
## Upcoming Features ## Upcoming Features
### Original Bit:Box Features (October 2024) ### Original Bit:Box Features (October 2024)

Binary file not shown.

BIN
assets/img/CSBox-Logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -1,293 +1,326 @@
--- ---
let initialBits = 8; const bitLabels = [128, 64, 32, 16, 8, 4, 2, 1];
--- ---
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Binary | Computing:Box</title> <title>Unsigned Binary | Computing:Box</title>
<style> <style>
:root{ :root{
--bg: #1f2027; --bg: #1f2027;
--panel: #22242d;
--text: #e8e8ee; --text: #e8e8ee;
--muted: #a9acb8; --muted: #a9acb8;
--accent: #33ff7a; --accent: #33ff7a;
--accent-dim:rgba(51,255,122,.2); --accent-dim: rgba(51,255,122,.15);
--line: rgba(255,255,255,.12); --line: rgba(255,255,255,.12);
} }
/* DSEG7ClassicRegular font */
@font-face{ @font-face{
font-family:"DSEG7"; font-family: "DSEG7ClassicRegular";
src:url("/fonts/DSEG7Classic-Regular.woff") format("woff"), src:
url("/fonts/DSEG7Classic-Regular.ttf") format("truetype"); url("/fonts/DSEG7Classic-Regular.ttf") format("truetype"),
url("/fonts/DSEG7Classic-Regular.woff") format("woff");
font-weight: 400;
font-style: normal;
font-display: swap;
} }
body{ body{
margin:0; margin:0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
background: var(--bg); background: var(--bg);
color: var(--text); color: var(--text);
font-family:system-ui,Segoe UI,Roboto,Arial;
} }
.wrap{ .wrap{
max-width: 1200px; max-width: 1200px;
margin:auto; margin: 0 auto;
padding: 32px 20px 60px; padding: 32px 20px 60px;
}
.top{
display:grid;
grid-template-columns: 1fr;
gap: 22px;
align-items:start;
}
.readout{
background: transparent;
text-align:center; text-align:center;
padding: 10px 10px 0;
} }
.label{ .label{
letter-spacing: .18em; letter-spacing: .18em;
color:var(--muted);
font-weight: 700; font-weight: 700;
color: var(--muted);
text-transform: uppercase;
font-size: 16px;
margin-top: 8px;
} }
/* All numbers use DSEG */
.num{ .num{
font-family:"DSEG7",monospace; font-family: "DSEG7ClassicRegular", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-weight: 400;
color: var(--accent); color: var(--accent);
text-shadow: 0 0 18px var(--accent-dim); text-shadow: 0 0 18px var(--accent-dim);
} }
.den{ .value{
font-size: 64px; font-size: 64px;
line-height: 1.0;
margin: 6px 0 16px;
} }
.bin{ .binary{
font-size: 44px; font-size: 44px;
letter-spacing: .12em; letter-spacing: .12em;
margin-bottom:12px;
} }
/* Buttons now UNDER the readout */
.controls{ .controls{
margin-top: 16px;
display:flex; display:flex;
gap:10px; gap: 12px;
justify-content:center; justify-content:center;
flex-wrap:wrap; flex-wrap:wrap;
margin:16px 0;
} }
.btn{ .btn{
background:rgba(255,255,255,.08); background: rgba(255,255,255,.06);
border:1px solid rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.14);
color: #fff; color: #fff;
padding:10px 14px; padding: 12px 14px;
border-radius: 12px; border-radius: 12px;
cursor:pointer;
font-weight: 700; font-weight: 700;
cursor: pointer;
min-width: 160px;
} }
.btn:active{ transform: translateY(1px); }
.panel{
margin-top:20px;
display:flex;
gap:24px;
justify-content:center;
flex-wrap:wrap;
}
.card{
background:rgba(255,255,255,.04);
border:1px solid var(--line);
border-radius:14px;
padding:14px 16px;
min-width:220px;
text-align:left;
}
.card h4{
margin:0 0 8px;
font-size:13px;
letter-spacing:.14em;
color:var(--muted);
}
.bits{ .bits{
margin-top:28px; margin-top: 34px;
padding-top:24px; padding-top: 26px;
border-top: 1px solid var(--line); border-top: 1px solid var(--line);
display:flex; display:grid;
justify-content:center; grid-template-columns: repeat(8, 1fr);
gap:20px; gap: 18px;
flex-wrap:wrap; align-items:end;
text-align:center;
} }
.bit{ .bit{
display:flex; display:flex;
flex-direction:column; flex-direction:column;
align-items:center; align-items:center;
gap:8px; gap: 10px;
padding: 8px 4px;
} }
/* bulb */ .bitVal{
font-size: 34px;
color: var(--text);
opacity: .95;
}
/* Bulb */
.bulb{ .bulb{
width: 22px; width: 22px;
height: 22px; height: 22px;
border-radius: 50%; border-radius: 50%;
background: rgba(255,255,255,.08); background: rgba(255,255,255,.08);
border: 1px solid rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.12);
box-shadow: none;
margin-bottom: 6px;
} }
.bulb.on{ .bulb.on{
background: #ffd86b; background: #ffd86b;
box-shadow:0 0 18px rgba(255,216,107,.7); border-color: rgba(255,216,107,.7);
box-shadow: 0 0 18px rgba(255,216,107,.6);
} }
/* SAME SWITCH USED EVERYWHERE */ /* Light switch */
.switch{ .switch{
position: relative; position: relative;
width: 56px; width: 56px;
height:32px; height: 34px;
display:inline-block;
}
.switch input{
opacity:0;
width:0;
height:0;
} }
.switch input{opacity:0;width:0;height:0;}
.slider{ .slider{
position:absolute; position:absolute;
inset:0; inset:0;
background:rgba(255,255,255,.15); background: rgba(255,255,255,.10);
border: 1px solid rgba(255,255,255,.14);
border-radius: 999px; border-radius: 999px;
transition:.2s; transition: .18s ease;
} }
.slider::before{ .slider::before{
content:""; content:"";
position:absolute; position:absolute;
width:26px; height: 28px;
height:26px; width: 28px;
left: 3px; left: 3px;
top:3px; top: 2px;
background:white; background: rgba(255,255,255,.92);
border-radius: 50%; border-radius: 50%;
transition:.2s; transition: .18s ease;
} }
.switch input:checked + .slider{ .switch input:checked + .slider{
background:rgba(51,255,122,.4); background: rgba(51,255,122,.20);
border-color: rgba(51,255,122,.55);
} }
.switch input:checked + .slider::before{ .switch input:checked + .slider::before{
transform:translateX(24px); transform: translateX(22px);
background: var(--accent); background: var(--accent);
} }
@media (max-width: 900px){
.bits{ grid-template-columns: repeat(4, 1fr); }
.value{ font-size: 54px; }
.binary{ font-size: 36px; }
}
</style> </style>
</head> </head>
<body> <body>
<main class="wrap"> <main class="wrap">
<section class="top">
<div class="readout">
<div class="label">Denary</div>
<div id="denaryNumber" class="value num">0</div>
<div class="label">DENARY</div> <div class="label">Binary</div>
<div id="denary" class="num den">0</div> <div id="binaryNumber" class="value binary num">00000000</div>
<div class="label">BINARY</div>
<div id="binary" class="num bin">00000000</div>
<!-- Buttons moved HERE (underneath) -->
<div class="controls"> <div class="controls">
<button class="btn" id="customBinary">Custom Binary</button> <button class="btn" id="btnCustomBinary" type="button">Custom Binary</button>
<button class="btn" id="customDenary">Custom Denary</button> <button class="btn" id="btnCustomDenary" type="button">Custom Denary</button>
<button class="btn" id="shiftL">Left Shift</button> <button class="btn" id="btnShiftLeft" type="button">Left Shift</button>
<button class="btn" id="shiftR">Right Shift</button> <button class="btn" id="btnShiftRight" type="button">Right Shift</button>
</div> </div>
</div>
</section>
<div class="panel"> <section class="bits" aria-label="Bit switches">
<div class="card"> {bitLabels.map((v) => (
<h4>MODE</h4> <div class="bit">
Unsigned <div class="bulb" id={`bulb-${v}`} aria-hidden="true"></div>
<label class="switch">
<input type="checkbox" id="mode"> <!-- bit place value is a number too -->
<div class="bitVal num">{v}</div>
<label class="switch" aria-label={`Toggle bit ${v}`}>
<input type="checkbox" data-bit={v} />
<span class="slider"></span> <span class="slider"></span>
</label> </label>
Twos complement
</div> </div>
))}
<div class="card"> </section>
<h4>BIT WIDTH</h4>
<input id="bitCount" type="number" min="4" max="64" value="8"
style="width:100%;padding:6px;border-radius:8px;border:none;">
</div>
</div>
<section class="bits" id="bits"></section>
</main> </main>
<script type="module"> <script type="module">
let bits = 8; const bitLabels = [128, 64, 32, 16, 8, 4, 2, 1];
let twos = false; const bits = new Map(bitLabels.map(v => [v, false]));
let state = [];
const bitsEl = document.getElementById("bits"); function updateReadout(){
let denary = 0;
let binary = "";
function build(){ for (const v of bitLabels){
state = Array(bits).fill(false); const on = bits.get(v);
bitsEl.innerHTML = ""; if (on) denary += v;
for(let i=bits-1;i>=0;i--){ binary += on ? "1" : "0";
const val = 2**i; const bulb = document.getElementById(`bulb-${v}`);
const div = document.createElement("div"); if (bulb) bulb.classList.toggle("on", on);
div.className="bit";
div.innerHTML=`
<div class="bulb" id="b${i}"></div>
<div class="num">${val}</div>
<label class="switch">
<input type="checkbox" data-i="${i}">
<span class="slider"></span>
</label>`;
bitsEl.appendChild(div);
}
document.querySelectorAll('[data-i]').forEach(sw=>{
sw.onchange=()=>{state[sw.dataset.i]=sw.checked;update();}
});
update();
} }
function update(){ document.getElementById("denaryNumber").innerText = denary.toString();
let n=0; document.getElementById("binaryNumber").innerText = binary;
state.forEach((on,i)=>{ }
if(on) n+=2**i;
document.getElementById(`b${i}`).classList.toggle("on",on); function setFromBinary(bin){
const clean = bin.replace(/\s+/g, "");
if (!/^[01]+$/.test(clean)) return false;
const padded = clean.slice(-8).padStart(8, "0");
[...padded].forEach((ch, i) => {
const v = bitLabels[i];
const on = ch === "1";
bits.set(v, on);
const input = document.querySelector(`input[data-bit="${v}"]`);
if (input) input.checked = on;
}); });
if(twos && state[bits-1]) n-=2**bits; updateReadout();
return true;
document.getElementById("denary").textContent=n;
document.getElementById("binary").textContent=
state.slice().reverse().map(b=>b?1:0).join("");
} }
document.getElementById("mode").onchange=e=>{ function setFromDenary(n){
twos=e.target.checked; n = Number(n);
update(); if (!Number.isInteger(n) || n < 0 || n > 255) return false;
};
document.getElementById("bitCount").onchange=e=>{ for (const v of bitLabels){
const v=Math.max(4,Math.min(64,Number(e.target.value))); const on = n >= v;
bits=v; bits.set(v, on);
build(); if (on) n -= v;
};
document.getElementById("shiftL").onclick=()=>{ const input = document.querySelector(`input[data-bit="${v}"]`);
state.shift();state.push(false);update(); if (input) input.checked = on;
};
document.getElementById("shiftR").onclick=()=>{
state.pop();state.unshift(false);update();
};
document.getElementById("customBinary").onclick=()=>{
const v=prompt(`Enter ${bits}-bit binary`);
if(!v||!/^[01]+$/.test(v))return;
const p=v.padStart(bits,"0").slice(-bits);
state=p.split("").reverse().map(b=>b==="1");
build();
};
document.getElementById("customDenary").onclick=()=>{
const max=twos?(2**(bits-1)-1):(2**bits-1);
const min=twos?-(2**(bits-1)):0;
const n=Number(prompt(`Enter ${min} to ${max}`));
if(isNaN(n)||n<min||n>max)return;
let val=n<0?n+2**bits:n;
state=Array(bits).fill(false);
for(let i=0;i<bits;i++){
if(val>=2**i){state[i]=true;val-=2**i;}
} }
build();
};
build(); updateReadout();
return true;
}
function shiftLeft(){
const bin = document.getElementById("binaryNumber").innerText;
setFromBinary(bin.slice(1) + "0");
}
function shiftRight(){
const bin = document.getElementById("binaryNumber").innerText;
setFromBinary("0" + bin.slice(0, -1));
}
// switches
document.querySelectorAll('input[type="checkbox"][data-bit]').forEach(input => {
input.addEventListener("change", () => {
const v = Number(input.dataset.bit);
bits.set(v, input.checked);
updateReadout();
});
});
// buttons
document.getElementById("btnShiftLeft").addEventListener("click", shiftLeft);
document.getElementById("btnShiftRight").addEventListener("click", shiftRight);
document.getElementById("btnCustomBinary").addEventListener("click", () => {
const val = prompt("Enter an 8-bit binary number (e.g. 01011001):");
if (val === null) return;
if (!setFromBinary(val)) alert("Invalid input. Use only 0 and 1 (up to 8 digits).");
});
document.getElementById("btnCustomDenary").addEventListener("click", () => {
const val = prompt("Enter a denary number (0 to 255):");
if (val === null) return;
if (!setFromDenary(val)) alert("Invalid input. Enter an integer from 0 to 255.");
});
updateReadout();
</script> </script>
</body> </body>
</html> </html>