You've already forked computing-box
Compare commits
16 Commits
v26.03.01-
...
v26.03.12-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc3d6f0e48 | ||
|
61b24dc309
|
|||
|
1ec16aecb3
|
|||
|
bb8e0c1969
|
|||
|
09e0499ba3
|
|||
|
7ecd065305
|
|||
|
e0e72c17e8
|
|||
|
d4ffe30f9b
|
|||
|
93542748e6
|
|||
|
98671cdeee
|
|||
| e74a20ca81 | |||
|
|
4b21391232 | ||
| 5708a184d5 | |||
|
af131fc58a
|
|||
|
|
4a0e4d306a | ||
|
aa9e071d40
|
15
README.md
15
README.md
@@ -28,18 +28,19 @@ An evolution of Bit:Box & CS:Box to incorporate different elements of the UK Com
|
||||
- [X] XNOR Gate Simulator
|
||||
|
||||
### Wave 3 CS:Box Features (Spring 2026)
|
||||
- [ ] New User Interface (Responsive)
|
||||
- [X] New User Interface (Responsive)
|
||||
- [X] Two's Compliment Simulator
|
||||
- [X] Extended Binary Simulator (Custom bit sizes)
|
||||
- [X] Unified Binary Simulator (Unsigned & Two's Completment combined)
|
||||
- [ ] Extended Hexadecimal Simulator
|
||||
- [ ] Unified Hexadecimal Simulator (For GCSE & A Level Specification)
|
||||
- [ ] Enhanced Gate Simulator (Truth Table Creator)
|
||||
- [ ] Compound Gate Simulator
|
||||
- [ ] Computer Components Simulator
|
||||
- [X] Extended Hexadecimal Simulator
|
||||
- [X] Unified Hexadecimal Simulator (For GCSE & A Level Specification)
|
||||
- [X] Enhanced Gate Simulator (Truth Table Creator)
|
||||
- [X] Compound Gate Simulator
|
||||
- [X] Computer Components Simulator - Beta Available
|
||||
|
||||
## Version 1.0 Release Date: 1<sup>st</sup> September 2025
|
||||
## Version 2.0 Release Date (Goal): 1<sup>st</sup> May 2026
|
||||
## Version 2.0 Release Date (Beta): 1<sup>st</sup> March 2026
|
||||
## Version 2.0 Release Date (Full Release): 1<sup>st</sup> May 2026
|
||||
|
||||
Shield: [![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa]
|
||||
|
||||
|
||||
20
dist/binary/index.html
vendored
20
dist/binary/index.html
vendored
@@ -1,3 +1,19 @@
|
||||
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Binary Simulator | Computing:Box</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/binary.BbxrcYRT.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">COMPUTING:BOX</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">ABOUT</a> <a href="/binary">BINARY</a> <a href="/hexadecimal">HEXADECIMAL</a> <a href="/hex-colours">HEX COLOURS</a> <a href="/logic-gates">LOGIC GATES</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="binaryPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Bit switches"> <div class="bitsGrid" id="bitsGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="toggleRow"> <div class="toggleLabel" id="lblUnsigned">Unsigned</div> <label class="switch" aria-label="Toggle mode"> <input id="modeToggle" type="checkbox"> <span class="slider"></span> </label> <div class="toggleLabel" id="lblTwos">Two's complement</div> </div> <div class="hint" id="modeHint">
|
||||
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Binary Simulator | Computing:Box</title><script>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
_paq.push(["setCookieDomain", "*.www.computingbox.co.uk"]);
|
||||
_paq.push(["setDomains", ["*.www.computingbox.co.uk","*.csbox.mrdaviscsit.uk","*.csbox.mrlyall.co.uk","*.csbox.mrlyall.uk"]]);
|
||||
_paq.push(["enableCrossDomainLinking"]);
|
||||
_paq.push(["disableCookies"]);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//analytics.adcmnetworks.co.uk/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '2']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/about.DM-NXsTj.css">
|
||||
<link rel="stylesheet" href="/_astro/binary.9peKc0z2.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="binaryPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Bit switches"> <div class="bitsGrid" id="bitsGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="toggleRow"> <div class="toggleLabel" id="lblUnsigned">Unsigned</div> <label class="switch" aria-label="Toggle mode"> <input id="modeToggle" type="checkbox"> <span class="slider"></span> </label> <div class="toggleLabel" id="lblTwos">Two's complement</div> </div> <div class="hint" id="modeHint">
|
||||
Tip: In unsigned binary, all bits represent positive values.
|
||||
</div> <div class="subCard"> <div class="subTitle">Bit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnBitsDown" type="button" aria-label="Decrease bits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Bits</div> <input id="bitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="64" step="1" value="8" aria-label="Number of bits"> </div> <button class="miniBtn" id="btnBitsUp" type="button" aria-label="Increase bits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomBinary" type="button">Custom Binary</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <div class="toolRow2"> <button class="btn btnHalf" id="btnShiftLeft" type="button">Left Shift</button> <button class="btn btnHalf" id="btnShiftRight" type="button">Right Shift</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/binary.astro_astro_type_script_index_0_lang.C_c_A3x5.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div>COMPUTER SCIENCE CONCEPT SIMULATORS</div> <div>© 2026 COMPUTING:BOX • CREATED WITH ♥ BY MR LYALL</div> </div> </footer> </body></html>
|
||||
</div> <div class="subCard"> <div class="subTitle">Bit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnBitsDown" type="button" aria-label="Decrease bits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Bits</div> <input id="bitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="64" step="1" value="8" aria-label="Number of bits"> </div> <button class="miniBtn" id="btnBitsUp" type="button" aria-label="Increase bits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomBinary" type="button">Custom Binary</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <div class="toolRow2"> <button class="btn btnHalf" id="btnShiftLeft" type="button">Left Shift</button> <button class="btn btnHalf" id="btnShiftRight" type="button">Right Shift</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/binary.astro_astro_type_script_index_0_lang.C_c_A3x5.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> </body></html>
|
||||
20
dist/hexadecimal/index.html
vendored
20
dist/hexadecimal/index.html
vendored
@@ -1,3 +1,19 @@
|
||||
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Hexadecimal Simulator | Computing:Box</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/binary.BbxrcYRT.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">COMPUTING:BOX</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">ABOUT</a> <a href="/binary">BINARY</a> <a href="/hexadecimal">HEXADECIMAL</a> <a href="/hex-colours">HEX COLOURS</a> <a href="/logic-gates">LOGIC GATES</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="hexPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Hexadecimal</div> <div id="hexNumber" class="num hexValue">00</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Hexadecimal sliders"> <div class="hexGrid" id="hexGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="hint" style="margin-top: 0; margin-bottom: 14px;">
|
||||
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Hexadecimal Simulator | Computing:Box</title><script>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
_paq.push(["setCookieDomain", "*.www.computingbox.co.uk"]);
|
||||
_paq.push(["setDomains", ["*.www.computingbox.co.uk","*.csbox.mrdaviscsit.uk","*.csbox.mrlyall.co.uk","*.csbox.mrlyall.uk"]]);
|
||||
_paq.push(["enableCrossDomainLinking"]);
|
||||
_paq.push(["disableCookies"]);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//analytics.adcmnetworks.co.uk/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '2']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/about.DM-NXsTj.css">
|
||||
<link rel="stylesheet" href="/_astro/binary.9peKc0z2.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div class="binaryPage" id="hexPage"> <button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true"> <span class="toolboxIcon" aria-hidden="true">🧰</span> <span class="toolboxText">TOOLBOX</span> </button> <section class="topGrid"> <div class="leftCol"> <div class="readout"> <div class="label">Denary</div> <div id="denaryNumber" class="num denaryValue">0</div> <div class="label">Hexadecimal</div> <div id="hexNumber" class="num hexValue">00</div> <div class="label">Binary</div> <div id="binaryNumber" class="num binaryValue">00000000</div> </div> <div class="divider"></div> <section class="bitsWrap" aria-label="Hexadecimal sliders"> <div class="hexGrid" id="hexGrid"></div> </section> </div> <aside id="toolboxPanel" class="panelCol" aria-label="Toolbox"> <div class="card"> <div class="cardTitle">Settings</div> <div class="hint" style="margin-top: 0; margin-bottom: 14px;">
|
||||
Hexadecimal represents numbers using base 16 (0-9, A-F).
|
||||
</div> <div class="subCard"> <div class="subTitle">Digit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnDigitsDown" type="button" aria-label="Decrease digits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Digits</div> <input id="digitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="16" step="1" value="2" aria-label="Number of hex digits"> </div> <button class="miniBtn" id="btnDigitsUp" type="button" aria-label="Increase digits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomHex" type="button">Custom Hex</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <div class="controlsRow"> <button class="btn btnAccent btnWide" id="btnCustomBinary" type="button">Custom Binary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/hexadecimal.astro_astro_type_script_index_0_lang.C4Wx7oaX.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div>COMPUTER SCIENCE CONCEPT SIMULATORS</div> <div>© 2026 COMPUTING:BOX • CREATED WITH ♥ BY MR LYALL</div> </div> </footer> </body></html>
|
||||
</div> <div class="subCard"> <div class="subTitle">Digit width</div> <div class="bitWidthRow"> <button class="miniBtn" id="btnDigitsDown" type="button" aria-label="Decrease digits">−</button> <div class="bitInputWrap"> <div class="bitInputLabel">Digits</div> <input id="digitsInput" class="bitInput" type="number" inputmode="numeric" min="1" max="16" step="1" value="2" aria-label="Number of hex digits"> </div> <button class="miniBtn" id="btnDigitsUp" type="button" aria-label="Increase digits">+</button> </div> </div> </div> <div class="card"> <div class="cardTitle">Custom Number</div> <div class="controlsRow"> <button class="btn btnAccent btnHalf" id="btnCustomHex" type="button">Custom Hex</button> <button class="btn btnAccent btnHalf" id="btnCustomDenary" type="button">Custom Denary</button> </div> <div class="controlsRow"> <button class="btn btnAccent btnWide" id="btnCustomBinary" type="button">Custom Binary</button> </div> <button class="btn btnWide" id="btnRandom" type="button">Random</button> <div class="hint">Random runs briefly then stops automatically.</div> </div> <div class="card"> <div class="cardTitle">Tools</div> <div class="toolRowCentered"> <button class="toolBtn toolSpin toolDec" id="btnDec" type="button" aria-label="Decrement">▼</button> <button class="toolBtn toolSpin toolInc" id="btnInc" type="button" aria-label="Increment">▲</button> </div> <button class="btn btnReset btnWide" id="btnClear" type="button">Reset</button> </div> </aside> </section> </div> <script type="module" src="/_astro/hexadecimal.astro_astro_type_script_index_0_lang.C4Wx7oaX.js"></script> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> </body></html>
|
||||
25
dist/index.html
vendored
25
dist/index.html
vendored
@@ -1,7 +1,18 @@
|
||||
<!DOCTYPE html><html lang="en" data-astro-cid-sckkx6r4> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><meta name="generator" content="Astro v5.18.0"><title>Astro Basics</title><style>#background[data-astro-cid-mmc7otgs]{position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;filter:blur(100px)}#container[data-astro-cid-mmc7otgs]{font-family:Inter,Roboto,Helvetica Neue,Arial Nova,Nimbus Sans,Arial,sans-serif;height:100%}main[data-astro-cid-mmc7otgs]{height:100%;display:flex;justify-content:center}#hero[data-astro-cid-mmc7otgs]{display:flex;align-items:start;flex-direction:column;justify-content:center;padding:16px}h1[data-astro-cid-mmc7otgs]{font-size:22px;margin-top:.25em}#links[data-astro-cid-mmc7otgs]{display:flex;gap:16px}#links[data-astro-cid-mmc7otgs] a[data-astro-cid-mmc7otgs]{display:flex;align-items:center;padding:10px 12px;color:#111827;text-decoration:none;transition:color .2s}#links[data-astro-cid-mmc7otgs] a[data-astro-cid-mmc7otgs]:hover{color:#4e5056}#links[data-astro-cid-mmc7otgs] a[data-astro-cid-mmc7otgs] svg[data-astro-cid-mmc7otgs]{height:1em;margin-left:8px}#links[data-astro-cid-mmc7otgs] a[data-astro-cid-mmc7otgs].button{color:#fff;background:linear-gradient(83.21deg,#3245ff,#bc52ee);box-shadow:inset 0 0 0 1px #ffffff1f,inset 0 -2px #0000003d;border-radius:10px}#links[data-astro-cid-mmc7otgs] a[data-astro-cid-mmc7otgs].button:hover{color:#e6e6e6;box-shadow:none}pre[data-astro-cid-mmc7otgs]{font-family:ui-monospace,Cascadia Code,Source Code Pro,Menlo,Consolas,DejaVu Sans Mono,monospace;font-weight:400;background:linear-gradient(14deg,#d83333,#f041ff);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin:0}h2[data-astro-cid-mmc7otgs]{margin:0 0 1em;font-weight:400;color:#111827;font-size:20px}p[data-astro-cid-mmc7otgs]{color:#4b5563;font-size:16px;line-height:24px;letter-spacing:-.006em;margin:0}code[data-astro-cid-mmc7otgs]{display:inline-block;background:linear-gradient(66.77deg,#f3cddd,#f5cee7) padding-box,linear-gradient(155deg,#d83333,#f041ff 18%,#f5cee7 45%) border-box;border-radius:8px;border:1px solid transparent;padding:6px 8px}.box[data-astro-cid-mmc7otgs]{padding:16px;background:#fff;border-radius:16px;border:1px solid white}#news[data-astro-cid-mmc7otgs]{position:absolute;bottom:16px;right:16px;max-width:300px;text-decoration:none;transition:background .2s;backdrop-filter:blur(50px)}#news[data-astro-cid-mmc7otgs]:hover{background:#ffffff8c}@media screen and (max-height:368px){#news[data-astro-cid-mmc7otgs]{display:none}}@media screen and (max-width:768px){#container[data-astro-cid-mmc7otgs]{display:flex;flex-direction:column}#hero[data-astro-cid-mmc7otgs]{display:block;padding-top:10%}#links[data-astro-cid-mmc7otgs]{flex-wrap:wrap}#links[data-astro-cid-mmc7otgs] a[data-astro-cid-mmc7otgs].button{padding:14px 18px}#news[data-astro-cid-mmc7otgs]{right:16px;left:16px;bottom:2.5rem;max-width:100%}h1[data-astro-cid-mmc7otgs]{line-height:1.5}}html,body{margin:0;width:100%;height:100%}
|
||||
</style></head> <body data-astro-cid-sckkx6r4> <div id="container" data-astro-cid-mmc7otgs> <img id="background" src="/_astro/background.BPKAcmfN.svg" alt="" fetchpriority="high" data-astro-cid-mmc7otgs> <main data-astro-cid-mmc7otgs> <section id="hero" data-astro-cid-mmc7otgs> <a href="https://astro.build" data-astro-cid-mmc7otgs><img src="/_astro/astro.Dm8K3lV8.svg" width="115" height="48" alt="Astro Homepage" data-astro-cid-mmc7otgs></a> <h1 data-astro-cid-mmc7otgs>
|
||||
To get started, open the <code data-astro-cid-mmc7otgs><pre data-astro-cid-mmc7otgs>src/pages</pre></code> directory in your project.
|
||||
</h1> <section id="links" data-astro-cid-mmc7otgs> <a class="button" href="https://docs.astro.build" data-astro-cid-mmc7otgs>Read our docs</a> <a href="https://astro.build/chat" data-astro-cid-mmc7otgs>Join our Discord <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36" data-astro-cid-mmc7otgs><path fill="currentColor" d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z" data-astro-cid-mmc7otgs></path></svg> </a> </section> </section> </main> <a href="https://astro.build/blog/astro-5/" id="news" class="box" data-astro-cid-mmc7otgs> <svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg" data-astro-cid-mmc7otgs><path d="M24.667 12c1.333 1.414 2 3.192 2 5.334 0 4.62-4.934 5.7-7.334 12C18.444 28.567 18 27.456 18 26c0-4.642 6.667-7.053 6.667-14Zm-5.334-5.333c1.6 1.65 2.4 3.43 2.4 5.333 0 6.602-8.06 7.59-6.4 17.334C13.111 27.787 12 25.564 12 22.666c0-4.434 7.333-8 7.333-16Zm-6-5.333C15.111 3.555 16 5.556 16 7.333c0 8.333-11.333 10.962-5.333 22-3.488-.774-6-4-6-8 0-8.667 8.666-10 8.666-20Z" fill="#111827" data-astro-cid-mmc7otgs></path></svg> <h2 data-astro-cid-mmc7otgs>What's New in Astro 5.0?</h2> <p data-astro-cid-mmc7otgs>
|
||||
From content layers to server islands, click to learn more about the new features and
|
||||
improvements in Astro 5.0
|
||||
</p> </a> </div> </body></html>
|
||||
<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Welcome | Computing:Box</title><script>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
_paq.push(["setCookieDomain", "*.www.computingbox.co.uk"]);
|
||||
_paq.push(["setDomains", ["*.www.computingbox.co.uk","*.csbox.mrdaviscsit.uk","*.csbox.mrlyall.co.uk","*.csbox.mrlyall.uk"]]);
|
||||
_paq.push(["enableCrossDomainLinking"]);
|
||||
_paq.push(["disableCookies"]);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//analytics.adcmnetworks.co.uk/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '2']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_astro/about.DM-NXsTj.css"></head> <body> <header class="siteNav"> <div class="navInner"> <a class="brand" href="/"> <img class="brandLogo" src="/images/computing-box-logo.svg" alt="Computing:Box logo"> <span class="brandName">Computing:Box</span> </a> <nav class="navLinks" aria-label="Site navigation"> <a href="/about">About</a> <a href="/binary">Binary</a> <a href="/hexadecimal">Hexadecimal</a> <a href="/hex-colours">Hex Colours</a> <a href="/logic-gates">Logic Gates</a> <a href="/pc-builder">PC Components</a> </nav> </div> </header> <main class="pageWrap"> <div style="display: flex; align-items: center; justify-content: space-between; gap: 40px; min-height: 60vh; padding: 40px 0;"> <div style="flex: 1;"> <p style="color: var(--accent); font-weight: 800; letter-spacing: 2px; text-transform: uppercase; margin-bottom: 10px;">Version 2.0 Now Live</p> <h1 class="brandName" style="font-size: 48px; line-height: 1.1; margin-bottom: 24px;">Understand Computing concepts better.</h1> <p style="font-size: 18px; color: var(--muted);">
|
||||
Interactive simulators for Binary, Hexadecimal, Logic Gates, and Computer Components designed for the UK curriculum.
|
||||
</p> <div style="display: flex; gap: 16px; margin-top: 32px;"> <a href="/about" class="btn btnAccent" style="text-decoration: none; padding: 14px 28px;">Learn More</a> <a href="/binary" class="btn" style="text-decoration: none; padding: 14px 28px;">Get Started</a> </div> </div> <div style="flex: 1; text-align: right;"> <img src="/images/computing-box-logo.svg" alt="Computing Box Logo" style="width: 100%; max-width: 450px; filter: drop-shadow(0 0 50px rgba(40, 240, 122, 0.15));"> </div> </div> </main> <footer class="siteFooter"> <div class="footerInner"> <div style="margin-top: 5px; display: flex; justify-content: center;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> <a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a> </div> <div>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Mr A Lyall</div> </div> </footer> </body></html>
|
||||
246
package-lock.json
generated
246
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "computing-box",
|
||||
"version": "0.0.1",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "computing-box",
|
||||
"version": "0.0.1",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"astro": "^5.18.0"
|
||||
}
|
||||
@@ -18,18 +18,18 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/internal-helpers": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.5.tgz",
|
||||
"integrity": "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==",
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.6.tgz",
|
||||
"integrity": "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@astrojs/markdown-remark": {
|
||||
"version": "6.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.10.tgz",
|
||||
"integrity": "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A==",
|
||||
"version": "6.3.11",
|
||||
"resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.11.tgz",
|
||||
"integrity": "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/internal-helpers": "0.7.5",
|
||||
"@astrojs/internal-helpers": "0.7.6",
|
||||
"@astrojs/prism": "3.3.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"hast-util-from-html": "^2.0.3",
|
||||
@@ -43,8 +43,8 @@
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.2",
|
||||
"remark-smartypants": "^3.0.2",
|
||||
"shiki": "^3.19.0",
|
||||
"smol-toml": "^1.5.2",
|
||||
"shiki": "^3.21.0",
|
||||
"smol-toml": "^1.6.0",
|
||||
"unified": "^11.0.5",
|
||||
"unist-util-remove-position": "^5.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
@@ -1661,14 +1661,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.18.0.tgz",
|
||||
"integrity": "sha512-CHiohwJIS4L0G6/IzE1Fx3dgWqXBCXus/od0eGUfxrZJD2um2pE7ehclMmgL/fXqbU7NfE1Ze2pq34h2QaA6iQ==",
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/astro/-/astro-5.18.1.tgz",
|
||||
"integrity": "sha512-m4VWilWZ+Xt6NPoYzC4CgGZim/zQUO7WFL0RHCH0AiEavF1153iC3+me2atDvXpf/yX4PyGUeD8wZLq1cirT3g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "^2.13.0",
|
||||
"@astrojs/internal-helpers": "0.7.5",
|
||||
"@astrojs/markdown-remark": "6.3.10",
|
||||
"@astrojs/internal-helpers": "0.7.6",
|
||||
"@astrojs/markdown-remark": "6.3.11",
|
||||
"@astrojs/telemetry": "3.3.0",
|
||||
"@capsizecss/unpack": "^4.0.0",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
@@ -1747,9 +1747,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz",
|
||||
"integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1763,9 +1763,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz",
|
||||
"integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1779,9 +1779,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1795,9 +1795,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1811,9 +1811,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1827,9 +1827,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1843,9 +1843,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1859,9 +1859,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1875,9 +1875,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
|
||||
"integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz",
|
||||
"integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1891,9 +1891,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1907,9 +1907,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz",
|
||||
"integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1923,9 +1923,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
|
||||
"integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz",
|
||||
"integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@@ -1939,9 +1939,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
|
||||
"integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz",
|
||||
"integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
@@ -1955,9 +1955,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
|
||||
"integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz",
|
||||
"integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -1971,9 +1971,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
|
||||
"integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz",
|
||||
"integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1987,9 +1987,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
|
||||
"integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz",
|
||||
"integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -2003,9 +2003,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2019,9 +2019,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2035,9 +2035,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2051,9 +2051,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2067,9 +2067,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2083,9 +2083,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2099,9 +2099,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2115,9 +2115,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
|
||||
"integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz",
|
||||
"integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2131,9 +2131,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
|
||||
"integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz",
|
||||
"integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -2147,9 +2147,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
|
||||
"integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz",
|
||||
"integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2163,9 +2163,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/astro/node_modules/esbuild": {
|
||||
"version": "0.27.3",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
|
||||
"integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
|
||||
"version": "0.27.4",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz",
|
||||
"integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
@@ -2175,32 +2175,32 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.27.3",
|
||||
"@esbuild/android-arm": "0.27.3",
|
||||
"@esbuild/android-arm64": "0.27.3",
|
||||
"@esbuild/android-x64": "0.27.3",
|
||||
"@esbuild/darwin-arm64": "0.27.3",
|
||||
"@esbuild/darwin-x64": "0.27.3",
|
||||
"@esbuild/freebsd-arm64": "0.27.3",
|
||||
"@esbuild/freebsd-x64": "0.27.3",
|
||||
"@esbuild/linux-arm": "0.27.3",
|
||||
"@esbuild/linux-arm64": "0.27.3",
|
||||
"@esbuild/linux-ia32": "0.27.3",
|
||||
"@esbuild/linux-loong64": "0.27.3",
|
||||
"@esbuild/linux-mips64el": "0.27.3",
|
||||
"@esbuild/linux-ppc64": "0.27.3",
|
||||
"@esbuild/linux-riscv64": "0.27.3",
|
||||
"@esbuild/linux-s390x": "0.27.3",
|
||||
"@esbuild/linux-x64": "0.27.3",
|
||||
"@esbuild/netbsd-arm64": "0.27.3",
|
||||
"@esbuild/netbsd-x64": "0.27.3",
|
||||
"@esbuild/openbsd-arm64": "0.27.3",
|
||||
"@esbuild/openbsd-x64": "0.27.3",
|
||||
"@esbuild/openharmony-arm64": "0.27.3",
|
||||
"@esbuild/sunos-x64": "0.27.3",
|
||||
"@esbuild/win32-arm64": "0.27.3",
|
||||
"@esbuild/win32-ia32": "0.27.3",
|
||||
"@esbuild/win32-x64": "0.27.3"
|
||||
"@esbuild/aix-ppc64": "0.27.4",
|
||||
"@esbuild/android-arm": "0.27.4",
|
||||
"@esbuild/android-arm64": "0.27.4",
|
||||
"@esbuild/android-x64": "0.27.4",
|
||||
"@esbuild/darwin-arm64": "0.27.4",
|
||||
"@esbuild/darwin-x64": "0.27.4",
|
||||
"@esbuild/freebsd-arm64": "0.27.4",
|
||||
"@esbuild/freebsd-x64": "0.27.4",
|
||||
"@esbuild/linux-arm": "0.27.4",
|
||||
"@esbuild/linux-arm64": "0.27.4",
|
||||
"@esbuild/linux-ia32": "0.27.4",
|
||||
"@esbuild/linux-loong64": "0.27.4",
|
||||
"@esbuild/linux-mips64el": "0.27.4",
|
||||
"@esbuild/linux-ppc64": "0.27.4",
|
||||
"@esbuild/linux-riscv64": "0.27.4",
|
||||
"@esbuild/linux-s390x": "0.27.4",
|
||||
"@esbuild/linux-x64": "0.27.4",
|
||||
"@esbuild/netbsd-arm64": "0.27.4",
|
||||
"@esbuild/netbsd-x64": "0.27.4",
|
||||
"@esbuild/openbsd-arm64": "0.27.4",
|
||||
"@esbuild/openbsd-x64": "0.27.4",
|
||||
"@esbuild/openharmony-arm64": "0.27.4",
|
||||
"@esbuild/sunos-x64": "0.27.4",
|
||||
"@esbuild/win32-arm64": "0.27.4",
|
||||
"@esbuild/win32-ia32": "0.27.4",
|
||||
"@esbuild/win32-x64": "0.27.4"
|
||||
}
|
||||
},
|
||||
"node_modules/axobject-query": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "computing-box",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"version": "2.0.0",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
|
||||
BIN
public/images/BitBoxLogo.png
Normal file
BIN
public/images/BitBoxLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
public/images/Educational_Impact.webp
Normal file
BIN
public/images/Educational_Impact.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 425 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 732 KiB After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/computingbox-concept-illustration.webp
Normal file
BIN
public/images/computingbox-concept-illustration.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="115" height="48"><path fill="#17191E" d="M7.77 36.35C6.4 35.11 6 32.51 6.57 30.62c.99 1.2 2.35 1.57 3.75 1.78 2.18.33 4.31.2 6.33-.78.23-.12.44-.27.7-.42.18.55.23 1.1.17 1.67a4.56 4.56 0 0 1-1.94 3.23c-.43.32-.9.61-1.34.91-1.38.94-1.76 2.03-1.24 3.62l.05.17a3.63 3.63 0 0 1-1.6-1.38 3.87 3.87 0 0 1-.63-2.1c0-.37 0-.74-.05-1.1-.13-.9-.55-1.3-1.33-1.32a1.56 1.56 0 0 0-1.63 1.26c0 .06-.03.12-.05.2Z"/><path fill="url(#a)" d="M7.77 36.35C6.4 35.11 6 32.51 6.57 30.62c.99 1.2 2.35 1.57 3.75 1.78 2.18.33 4.31.2 6.33-.78.23-.12.44-.27.7-.42.18.55.23 1.1.17 1.67a4.56 4.56 0 0 1-1.94 3.23c-.43.32-.9.61-1.34.91-1.38.94-1.76 2.03-1.24 3.62l.05.17a3.63 3.63 0 0 1-1.6-1.38 3.87 3.87 0 0 1-.63-2.1c0-.37 0-.74-.05-1.1-.13-.9-.55-1.3-1.33-1.32a1.56 1.56 0 0 0-1.63 1.26c0 .06-.03.12-.05.2Z"/><path fill="#17191E" d="M.02 30.31s4.02-1.95 8.05-1.95l3.04-9.4c.11-.45.44-.76.82-.76.37 0 .7.31.82.76l3.04 9.4c4.77 0 8.05 1.95 8.05 1.95L17 11.71c-.2-.56-.53-.91-.98-.91H7.83c-.44 0-.76.35-.97.9L.02 30.31Zm42.37-5.97c0 1.64-2.05 2.62-4.88 2.62-1.85 0-2.5-.45-2.5-1.41 0-1 .8-1.49 2.65-1.49 1.67 0 3.09.03 4.73.23v.05Zm.03-2.04a21.37 21.37 0 0 0-4.37-.36c-5.32 0-7.82 1.25-7.82 4.18 0 3.04 1.71 4.2 5.68 4.2 3.35 0 5.63-.84 6.46-2.92h.14c-.03.5-.05 1-.05 1.4 0 1.07.18 1.16 1.06 1.16h4.15a16.9 16.9 0 0 1-.36-4c0-1.67.06-2.93.06-4.62 0-3.45-2.07-5.64-8.56-5.64-2.8 0-5.9.48-8.26 1.19.22.93.54 2.83.7 4.06 2.04-.96 4.95-1.37 7.2-1.37 3.11 0 3.97.71 3.97 2.15v.57Zm11.37 3c-.56.07-1.33.07-2.12.07-.83 0-1.6-.03-2.12-.1l-.02.58c0 2.85 1.87 4.52 8.45 4.52 6.2 0 8.2-1.64 8.2-4.55 0-2.74-1.33-4.09-7.2-4.39-4.58-.2-4.99-.7-4.99-1.28 0-.66.59-1 3.65-1 3.18 0 4.03.43 4.03 1.35v.2a46.13 46.13 0 0 1 4.24.03l.02-.55c0-3.36-2.8-4.46-8.2-4.46-6.08 0-8.13 1.49-8.13 4.39 0 2.6 1.64 4.23 7.48 4.48 4.3.14 4.77.62 4.77 1.28 0 .7-.7 1.03-3.71 1.03-3.47 0-4.35-.48-4.35-1.47v-.13Zm19.82-12.05a17.5 17.5 0 0 1-6.24 3.48c.03.84.03 2.4.03 3.24l1.5.02c-.02 1.63-.04 3.6-.04 4.9 0 3.04 1.6 5.32 6.58 5.32 2.1 0 3.5-.23 5.23-.6a43.77 43.77 0 0 1-.46-4.13c-1.03.34-2.34.53-3.78.53-2 0-2.82-.55-2.82-2.13 0-1.37 0-2.65.03-3.84 2.57.02 5.13.07 6.64.11-.02-1.18.03-2.9.1-4.04-2.2.04-4.65.07-6.68.07l.07-2.93h-.16Zm13.46 6.04a767.33 767.33 0 0 1 .07-3.18H82.6c.07 1.96.07 3.98.07 6.92 0 2.95-.03 4.99-.07 6.93h5.18c-.09-1.37-.11-3.68-.11-5.65 0-3.1 1.26-4 4.12-4 1.33 0 2.28.16 3.1.46.03-1.16.26-3.43.4-4.43-.86-.25-1.81-.41-2.96-.41-2.46-.03-4.26.98-5.1 3.38l-.17-.02Zm22.55 3.65c0 2.5-1.8 3.66-4.64 3.66-2.81 0-4.61-1.1-4.61-3.66s1.82-3.52 4.61-3.52c2.82 0 4.64 1.03 4.64 3.52Zm4.71-.11c0-4.96-3.87-7.18-9.35-7.18-5.5 0-9.23 2.22-9.23 7.18 0 4.94 3.49 7.59 9.21 7.59 5.77 0 9.37-2.65 9.37-7.6Z"/><defs><linearGradient id="a" x1="6.33" x2="19.43" y1="40.8" y2="34.6" gradientUnits="userSpaceOnUse"><stop stop-color="#D83333"/><stop offset="1" stop-color="#F041FF"/></linearGradient></defs></svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
@@ -1,210 +0,0 @@
|
||||
---
|
||||
import astroLogo from '../assets/astro.svg';
|
||||
import background from '../assets/background.svg';
|
||||
---
|
||||
|
||||
<div id="container">
|
||||
<img id="background" src={background.src} alt="" fetchpriority="high" />
|
||||
<main>
|
||||
<section id="hero">
|
||||
<a href="https://astro.build"
|
||||
><img src={astroLogo.src} width="115" height="48" alt="Astro Homepage" /></a
|
||||
>
|
||||
<h1>
|
||||
To get started, open the <code><pre>src/pages</pre></code> directory in your project.
|
||||
</h1>
|
||||
<section id="links">
|
||||
<a class="button" href="https://docs.astro.build">Read our docs</a>
|
||||
<a href="https://astro.build/chat"
|
||||
>Join our Discord <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z"
|
||||
></path></svg
|
||||
>
|
||||
</a>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<a href="https://astro.build/blog/astro-5/" id="news" class="box">
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
d="M24.667 12c1.333 1.414 2 3.192 2 5.334 0 4.62-4.934 5.7-7.334 12C18.444 28.567 18 27.456 18 26c0-4.642 6.667-7.053 6.667-14Zm-5.334-5.333c1.6 1.65 2.4 3.43 2.4 5.333 0 6.602-8.06 7.59-6.4 17.334C13.111 27.787 12 25.564 12 22.666c0-4.434 7.333-8 7.333-16Zm-6-5.333C15.111 3.555 16 5.556 16 7.333c0 8.333-11.333 10.962-5.333 22-3.488-.774-6-4-6-8 0-8.667 8.666-10 8.666-20Z"
|
||||
fill="#111827"></path></svg
|
||||
>
|
||||
<h2>What's New in Astro 5.0?</h2>
|
||||
<p>
|
||||
From content layers to server islands, click to learn more about the new features and
|
||||
improvements in Astro 5.0
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
filter: blur(100px);
|
||||
}
|
||||
|
||||
#container {
|
||||
font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#hero {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 22px;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
#links {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
#links a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
color: #111827;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
#links a:hover {
|
||||
color: rgb(78, 80, 86);
|
||||
}
|
||||
|
||||
#links a svg {
|
||||
height: 1em;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
#links a.button {
|
||||
color: white;
|
||||
background: linear-gradient(83.21deg, #3245ff 0%, #bc52ee 100%);
|
||||
box-shadow:
|
||||
inset 0 0 0 1px rgba(255, 255, 255, 0.12),
|
||||
inset 0 -2px 0 rgba(0, 0, 0, 0.24);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
#links a.button:hover {
|
||||
color: rgb(230, 230, 230);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family:
|
||||
ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono',
|
||||
monospace;
|
||||
font-weight: normal;
|
||||
background: linear-gradient(14deg, #d83333 0%, #f041ff 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 1em;
|
||||
font-weight: normal;
|
||||
color: #111827;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #4b5563;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.006em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
display: inline-block;
|
||||
background:
|
||||
linear-gradient(66.77deg, #f3cddd 0%, #f5cee7 100%) padding-box,
|
||||
linear-gradient(155deg, #d83333 0%, #f041ff 18%, #f5cee7 45%) border-box;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 16px;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 16px;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
#news {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
max-width: 300px;
|
||||
text-decoration: none;
|
||||
transition: background 0.2s;
|
||||
backdrop-filter: blur(50px);
|
||||
}
|
||||
|
||||
#news:hover {
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
@media screen and (max-height: 368px) {
|
||||
#news {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
#container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#hero {
|
||||
display: block;
|
||||
padding-top: 10%;
|
||||
}
|
||||
|
||||
#links {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#links a.button {
|
||||
padding: 14px 18px;
|
||||
}
|
||||
|
||||
#news {
|
||||
right: 16px;
|
||||
left: 16px;
|
||||
bottom: 2.5rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,104 +0,0 @@
|
||||
---
|
||||
import "./hex/hex-simulator.css";
|
||||
---
|
||||
|
||||
<section class="hex-sim" data-hex-sim>
|
||||
<div class="hex-main">
|
||||
<div class="hex-readout">
|
||||
<div class="hex-label">DENARY</div>
|
||||
<div class="hex-number" data-out="denary">0</div>
|
||||
|
||||
<div class="hex-label hex-mt">HEXADECIMAL</div>
|
||||
<div class="hex-number hex-number--small" data-out="hex">00</div>
|
||||
|
||||
<div class="hex-label hex-mt">BINARY</div>
|
||||
<div class="hex-number hex-number--tiny" data-out="bin">0000 0000</div>
|
||||
</div>
|
||||
|
||||
<div class="hex-divider"></div>
|
||||
|
||||
<div class="hex-digits" data-out="digitsRow"></div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbox button -->
|
||||
<button class="hex-toolbox-btn" type="button" data-action="toggleToolbox" aria-controls="hex-toolbox" aria-expanded="true">
|
||||
<span class="hex-toolbox-icon" aria-hidden="true">
|
||||
<!-- toolbox icon -->
|
||||
<svg viewBox="0 0 24 24" width="18" height="18" fill="none">
|
||||
<path d="M9 7V6a3 3 0 0 1 6 0v1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M4 9h16l-1.3 10.4A2 2 0 0 1 16.7 21H7.3a2 2 0 0 1-1.98-1.6L4 9Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<path d="M10 13h4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</span>
|
||||
TOOLBOX
|
||||
</button>
|
||||
|
||||
<!-- Toolbox panel -->
|
||||
<aside class="hex-toolbox is-open" id="hex-toolbox" data-out="toolbox">
|
||||
<div class="hex-panel">
|
||||
<div class="hex-panel-title">SETTINGS</div>
|
||||
|
||||
<div class="hex-setting-title">HEX DIGIT WIDTH</div>
|
||||
|
||||
<div class="hex-width">
|
||||
<button class="hex-btn hex-btn--square" type="button" data-action="digitsMinus">−</button>
|
||||
|
||||
<div class="hex-width-readout">
|
||||
<div class="hex-width-label">DIGITS</div>
|
||||
<div class="hex-width-number" data-out="digitsCount">2</div>
|
||||
</div>
|
||||
|
||||
<button class="hex-btn hex-btn--square" type="button" data-action="digitsPlus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="hex-hint" data-out="bitsHint">= 8 bits</div>
|
||||
</div>
|
||||
|
||||
<div class="hex-panel">
|
||||
<div class="hex-panel-title">CUSTOM NUMBER</div>
|
||||
|
||||
<div class="hex-grid-2">
|
||||
<button class="hex-btn hex-btn--green" type="button" data-action="customHex">Custom Hexadecimal</button>
|
||||
<button class="hex-btn hex-btn--green" type="button" data-action="customDenary">Custom Denary</button>
|
||||
</div>
|
||||
|
||||
<!-- Custom Binary + Random on SAME row, same size -->
|
||||
<div class="hex-grid-2 hex-mt-sm">
|
||||
<button class="hex-btn hex-btn--green" type="button" data-action="customBinary">Custom Binary</button>
|
||||
<button class="hex-btn hex-btn--wide hex-btn--random" type="button" data-action="random" data-random>Random</button>
|
||||
</div>
|
||||
|
||||
<div class="hex-tiny-note">RANDOM RUNS BRIEFLY THEN STOPS AUTOMATICALLY.</div>
|
||||
</div>
|
||||
|
||||
<div class="hex-panel">
|
||||
<div class="hex-panel-title">TOOLS</div>
|
||||
|
||||
<div class="hex-tools-top">
|
||||
<button class="hex-btn hex-btn--square hex-btn--red" type="button" data-action="decrement" title="Decrement">▼</button>
|
||||
<button class="hex-btn hex-btn--square hex-btn--green2" type="button" data-action="increment" title="Increment">▲</button>
|
||||
</div>
|
||||
|
||||
<button class="hex-btn hex-btn--wide hex-btn--reset" type="button" data-action="reset">Reset</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Custom number dialog -->
|
||||
<dialog class="hex-dialog" data-out="dialog">
|
||||
<div class="hex-dialog-card">
|
||||
<div class="hex-dialog-title" data-out="dialogTitle">Custom</div>
|
||||
|
||||
<input class="hex-dialog-input hex-font-mono" data-out="dialogInput" />
|
||||
|
||||
<div class="hex-dialog-hint" data-out="dialogHint"></div>
|
||||
<div class="hex-dialog-error" data-out="dialogError" aria-live="polite"></div>
|
||||
|
||||
<div class="hex-dialog-actions">
|
||||
<button class="hex-btn" type="button" data-action="dialogCancel">Cancel</button>
|
||||
<button class="hex-btn hex-btn--green" type="button" data-action="dialogApply">Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<script type="module" src="/src/components/simulators/hex/hex-simulator.ts"></script>
|
||||
</section>
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
type DialogMode = "hex" | "den" | "bin";
|
||||
|
||||
const root = document.querySelector<HTMLElement>("[data-hex-sim]");
|
||||
if (!root) throw new Error("Hex simulator root not found");
|
||||
|
||||
const outDen = root.querySelector<HTMLElement>('[data-out="denary"]')!;
|
||||
const outHex = root.querySelector<HTMLElement>('[data-out="hex"]')!;
|
||||
const outBin = root.querySelector<HTMLElement>('[data-out="bin"]')!;
|
||||
const outDigitsRow = root.querySelector<HTMLElement>('[data-out="digitsRow"]')!;
|
||||
|
||||
const toolbox = root.querySelector<HTMLElement>('[data-out="toolbox"]')!;
|
||||
const toolboxBtn = root.querySelector<HTMLButtonElement>('[data-action="toggleToolbox"]')!;
|
||||
const digitsCount = root.querySelector<HTMLElement>('[data-out="digitsCount"]')!;
|
||||
const bitsHint = root.querySelector<HTMLElement>('[data-out="bitsHint"]')!;
|
||||
const randomBtn = root.querySelector<HTMLButtonElement>("[data-random]")!;
|
||||
|
||||
const dialog = root.querySelector<HTMLDialogElement>('[data-out="dialog"]')!;
|
||||
const dialogTitle = root.querySelector<HTMLElement>('[data-out="dialogTitle"]')!;
|
||||
const dialogInput = root.querySelector<HTMLInputElement>('[data-out="dialogInput"]')!;
|
||||
const dialogHint = root.querySelector<HTMLElement>('[data-out="dialogHint"]')!;
|
||||
const dialogError = root.querySelector<HTMLElement>('[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 = `
|
||||
<div class="hex-digit-controls">
|
||||
<button class="hex-btn hex-btn--square hex-btn--green2" type="button" data-action="digitUp" data-i="${i}" title="Increase">▲</button>
|
||||
<button class="hex-btn hex-btn--square hex-btn--red" type="button" data-action="digitDown" data-i="${i}" title="Decrease">▼</button>
|
||||
</div>
|
||||
|
||||
<div class="hex-digit-char hex-font-number">${digitChar}</div>
|
||||
|
||||
<!-- bulbs: brightness changes based on nibble bits -->
|
||||
<div class="hex-bulbs" aria-label="Nibble bits">
|
||||
${[8,4,2,1].map((w, idx) => {
|
||||
const on = nibbleBits[idx] === 1;
|
||||
return `
|
||||
<div class="hex-bulb ${on ? "is-on" : ""}">
|
||||
<div class="hex-bulb-cap"></div>
|
||||
<div class="hex-bulb-glow"></div>
|
||||
<div class="hex-bulb-label">${w}</div>
|
||||
</div>
|
||||
`;
|
||||
}).join("")}
|
||||
</div>
|
||||
|
||||
<div class="hex-digit-place">${placeValue}</div>
|
||||
`;
|
||||
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<HTMLElement>("[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();
|
||||
@@ -1,8 +0,0 @@
|
||||
<footer class="site-footer">
|
||||
<div class="site-footer__inner">
|
||||
<div class="site-footer__text">
|
||||
COMPUTER SCIENCE CONCEPT SIMULATORS<br />
|
||||
© 2025 COMPUTING:BOX • CREATED WITH ♥ BY MR LYALL
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -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" },
|
||||
];
|
||||
---
|
||||
<header class="site-header">
|
||||
<div class="site-header__inner">
|
||||
<a class="site-header__brand" href="/" aria-label="Computing:Box home">
|
||||
<img class="site-header__logo" src="/img/logo.png" alt="" width="26" height="26" />
|
||||
<span class="site-header__name">COMPUTING:BOX</span>
|
||||
</a>
|
||||
|
||||
<nav class="site-header__nav" aria-label="Primary">
|
||||
{nav.map((i) => (
|
||||
<a class="site-header__link" href={i.href}>
|
||||
{i.label.toUpperCase()}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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);
|
||||
---
|
||||
|
||||
<section class="binary-sim" data-mode={mode} data-bits={initialBits}>
|
||||
<div class="top">
|
||||
<div class="left-brand">
|
||||
<!-- Replace with your logo/img -->
|
||||
<div class="brand-box" aria-hidden="true">Computing:Box</div>
|
||||
</div>
|
||||
|
||||
<div class="display" aria-live="polite">
|
||||
<div class="label">DENARY</div>
|
||||
<div id="denaryNumber" class="value">0</div>
|
||||
|
||||
<div class="label">BINARY</div>
|
||||
<div id="binaryNumber" class="value mono">00000000</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn" type="button" data-action="custom-binary">Custom Binary</button>
|
||||
<button class="btn" type="button" data-action="custom-denary">Custom Denary</button>
|
||||
<button class="btn" type="button" data-action="shift-left">Left Shift</button>
|
||||
<button class="btn" type="button" data-action="shift-right">Right Shift</button>
|
||||
|
||||
<div class="bits-control">
|
||||
<div class="bits-title">Bits</div>
|
||||
<div class="bits-buttons">
|
||||
<button class="btn small" type="button" data-action="bits-minus" aria-label="Decrease bits">−</button>
|
||||
<span id="bitsCount" class="bits-count">{initialBits}</span>
|
||||
<button class="btn small" type="button" data-action="bits-plus" aria-label="Increase bits">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bitsRow" class="bits-row" aria-label="Binary bit switches"></div>
|
||||
</section>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const root = document.currentScript.closest(".binary-sim");
|
||||
const bitsRow = root.querySelector("#bitsRow");
|
||||
const binaryEl = root.querySelector("#binaryNumber");
|
||||
const denaryEl = root.querySelector("#denaryNumber");
|
||||
const bitsCountEl = root.querySelector("#bitsCount");
|
||||
|
||||
let bitCount = parseInt(root.dataset.bits || "8", 10);
|
||||
const minBits = 4;
|
||||
const maxBits = 16;
|
||||
|
||||
// state: array of 0/1, MSB at index 0
|
||||
let bits = new Array(bitCount).fill(0);
|
||||
|
||||
function placeValues(n) {
|
||||
// unsigned place values: [2^(n-1), ..., 2^0]
|
||||
return Array.from({ length: n }, (_, i) => 2 ** (n - 1 - i));
|
||||
}
|
||||
|
||||
function bitsToDenary(bitsArr) {
|
||||
const pv = placeValues(bitsArr.length);
|
||||
return bitsArr.reduce((acc, b, i) => acc + (b ? pv[i] : 0), 0);
|
||||
}
|
||||
|
||||
function render() {
|
||||
const binaryStr = bits.join("");
|
||||
binaryEl.textContent = binaryStr;
|
||||
denaryEl.textContent = String(bitsToDenary(bits));
|
||||
bitsCountEl.textContent = String(bitCount);
|
||||
|
||||
const pv = placeValues(bitCount);
|
||||
|
||||
bitsRow.innerHTML = pv.map((value, i) => {
|
||||
const id = `bit_${bitCount}_${i}`;
|
||||
const checked = bits[i] === 1 ? "checked" : "";
|
||||
return `
|
||||
<div class="bit-col">
|
||||
<div class="bulb ${bits[i] ? "on" : "off"}" aria-hidden="true"></div>
|
||||
<div class="place">${value}</div>
|
||||
|
||||
<label class="switch" for="${id}">
|
||||
<input id="${id}" type="checkbox" ${checked} data-index="${i}">
|
||||
<span class="slider" aria-hidden="true"></span>
|
||||
<span class="sr-only">Toggle bit value ${value}</span>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function setBitsFromBinaryString(str) {
|
||||
// allow shorter input; pad left
|
||||
const clean = (str || "").trim();
|
||||
if (!/^[01]+$/.test(clean) || clean.length > bitCount) return false;
|
||||
const padded = clean.padStart(bitCount, "0");
|
||||
bits = padded.split("").map(c => c === "1" ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
function setBitsFromDenary(num) {
|
||||
if (!Number.isInteger(num)) return false;
|
||||
const max = (2 ** bitCount) - 1;
|
||||
if (num < 0 || num > max) return false;
|
||||
|
||||
const bin = num.toString(2).padStart(bitCount, "0");
|
||||
bits = bin.split("").map(c => c === "1" ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
function shiftLeft() {
|
||||
bits = bits.slice(1).concat(0);
|
||||
}
|
||||
|
||||
function shiftRight() {
|
||||
bits = [0].concat(bits.slice(0, -1));
|
||||
}
|
||||
|
||||
function resizeBits(newCount) {
|
||||
const clamped = Math.max(minBits, Math.min(maxBits, newCount));
|
||||
if (clamped === bitCount) return;
|
||||
|
||||
// keep value as best as possible: preserve LSBs when resizing
|
||||
const currentBinary = bits.join("");
|
||||
bitCount = clamped;
|
||||
bits = new Array(bitCount).fill(0);
|
||||
|
||||
const take = currentBinary.slice(-bitCount);
|
||||
setBitsFromBinaryString(take);
|
||||
}
|
||||
|
||||
// Switch input handler
|
||||
bitsRow.addEventListener("change", (e) => {
|
||||
const input = e.target;
|
||||
if (!(input instanceof HTMLInputElement)) return;
|
||||
const idx = parseInt(input.dataset.index || "-1", 10);
|
||||
if (idx < 0) return;
|
||||
bits[idx] = input.checked ? 1 : 0;
|
||||
render();
|
||||
});
|
||||
|
||||
// Button actions
|
||||
root.querySelector(".actions").addEventListener("click", (e) => {
|
||||
const btn = e.target.closest("button[data-action]");
|
||||
if (!btn) return;
|
||||
|
||||
const action = btn.dataset.action;
|
||||
|
||||
if (action === "custom-binary") {
|
||||
const entered = prompt(`Enter a ${bitCount}-bit binary value (0s and 1s):`, bits.join(""));
|
||||
if (entered === null) return;
|
||||
if (!setBitsFromBinaryString(entered)) {
|
||||
alert(`Invalid binary. Use only 0 and 1, up to ${bitCount} digits.`);
|
||||
return;
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
if (action === "custom-denary") {
|
||||
const max = (2 ** bitCount) - 1;
|
||||
const entered = prompt(`Enter a denary value (0 to ${max}):`, String(bitsToDenary(bits)));
|
||||
if (entered === null) return;
|
||||
const num = Number.parseInt(entered, 10);
|
||||
if (!setBitsFromDenary(num)) {
|
||||
alert(`Invalid denary. Enter a whole number from 0 to ${max}.`);
|
||||
return;
|
||||
}
|
||||
render();
|
||||
}
|
||||
|
||||
if (action === "shift-left") { shiftLeft(); render(); }
|
||||
if (action === "shift-right") { shiftRight(); render(); }
|
||||
|
||||
if (action === "bits-minus") { resizeBits(bitCount - 1); render(); }
|
||||
if (action === "bits-plus") { resizeBits(bitCount + 1); render(); }
|
||||
});
|
||||
|
||||
// initial
|
||||
render();
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Layout */
|
||||
.binary-sim {
|
||||
padding: 2rem 1rem 3rem;
|
||||
}
|
||||
|
||||
.top {
|
||||
display: grid;
|
||||
grid-template-columns: 260px 1fr 220px;
|
||||
gap: 1.5rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.top {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Brand placeholder */
|
||||
.brand-box {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
border-radius: 12px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-weight: 700;
|
||||
opacity: 0.9;
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
/* Display */
|
||||
.display {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.label {
|
||||
letter-spacing: 0.12em;
|
||||
opacity: 0.85;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 3rem;
|
||||
line-height: 1.1;
|
||||
margin: 0.25rem 0 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
padding: 0.75rem 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
background: rgba(255,255,255,0.08);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.btn:hover { background: rgba(255,255,255,0.12); }
|
||||
|
||||
.btn.small {
|
||||
padding: 0.45rem 0.75rem;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.bits-control {
|
||||
margin-top: 0.25rem;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
.bits-title { opacity: 0.8; margin-bottom: 0.35rem; }
|
||||
|
||||
.bits-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.bits-count {
|
||||
min-width: 2ch;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Bits row */
|
||||
.bits-row {
|
||||
margin-top: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
|
||||
gap: 1.25rem;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.bit-col {
|
||||
text-align: center;
|
||||
padding: 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
.place {
|
||||
margin: 0.5rem 0 0.5rem;
|
||||
font-size: 2rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
/* Bulb */
|
||||
.bulb {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin: 0 auto 0.25rem;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
}
|
||||
.bulb.off { opacity: 0.15; }
|
||||
.bulb.on { opacity: 1; }
|
||||
|
||||
/* Light switch (accessible checkbox) */
|
||||
.switch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64px;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.slider {
|
||||
width: 64px;
|
||||
height: 36px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.10);
|
||||
border: 1px solid rgba(255,255,255,0.14);
|
||||
position: relative;
|
||||
transition: transform 120ms ease, background 120ms ease;
|
||||
}
|
||||
|
||||
.slider::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.85);
|
||||
transition: transform 120ms ease;
|
||||
}
|
||||
|
||||
.switch input:checked + .slider {
|
||||
background: rgba(255,255,255,0.18);
|
||||
}
|
||||
|
||||
.switch input:checked + .slider::after {
|
||||
transform: translateX(28px);
|
||||
}
|
||||
|
||||
/* focus */
|
||||
.switch input:focus-visible + .slider {
|
||||
outline: 3px solid rgba(255,255,255,0.35);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden; clip: rect(0,0,0,0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
import '../styles/global.css';
|
||||
|
||||
import "../styles/global.css";
|
||||
const { title = "Computing:Box" } = Astro.props;
|
||||
---
|
||||
|
||||
@@ -11,6 +10,22 @@ const { title = "Computing:Box" } = Astro.props;
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>{title}</title>
|
||||
|
||||
<script is:inline>
|
||||
var _paq = window._paq = window._paq || [];
|
||||
_paq.push(["setCookieDomain", "*.www.computingbox.co.uk"]);
|
||||
_paq.push(["setDomains", ["*.www.computingbox.co.uk","*.csbox.mrdaviscsit.uk","*.csbox.mrlyall.co.uk","*.csbox.mrlyall.uk"]]);
|
||||
_paq.push(["enableCrossDomainLinking"]);
|
||||
_paq.push(["disableCookies"]);
|
||||
_paq.push(['trackPageView']);
|
||||
_paq.push(['enableLinkTracking']);
|
||||
(function() {
|
||||
var u="//analytics.adcmnetworks.co.uk/";
|
||||
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||
_paq.push(['setSiteId', '2']);
|
||||
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||
g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||
})();
|
||||
</script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700;800;900&display=swap" rel="stylesheet">
|
||||
@@ -29,6 +44,7 @@ const { title = "Computing:Box" } = Astro.props;
|
||||
<a href="/hexadecimal">Hexadecimal</a>
|
||||
<a href="/hex-colours">Hex Colours</a>
|
||||
<a href="/logic-gates">Logic Gates</a>
|
||||
<a href="/pc-builder">PC Components</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
@@ -39,6 +55,10 @@ const { title = "Computing:Box" } = Astro.props;
|
||||
|
||||
<footer class="siteFooter">
|
||||
<div class="footerInner">
|
||||
<div style="margin-top: 5px; display: flex; justify-content: center;">
|
||||
<a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a>
|
||||
<a href="/legal-code" style="margin-left: 32px; color: var(--muted); text-decoration: underline;">Legal Code</a>
|
||||
</div>
|
||||
<div>Computer Science Concept Simulators</div>
|
||||
<div>© {new Date().getFullYear()} Computing:Box • Created with ♥ by Mr A Lyall</div>
|
||||
</div>
|
||||
|
||||
55
src/pages/about.astro
Normal file
55
src/pages/about.astro
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
---
|
||||
|
||||
<BaseLayout title="About - Computing:Box">
|
||||
<article style="max-width: 1100px; margin: 0 auto; width: 100%;">
|
||||
|
||||
<div style="text-align: center; margin-bottom: 60px;">
|
||||
<img src="/images/computing-box-logo.svg" alt="Computing:Box Logo" style="width: 250px; border-radius: 20px; box-shadow: 0 12px 40px rgba(0,0,0,0.5);" />
|
||||
<h1 class="brandName" style="font-size: 42px; margin-top: 20px; color: var(--text);">The New Computing:Box Experience</h1>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom: 40px;">
|
||||
<p style="color: var(--muted); font-size: 18px; line-height: 1.6; text-align: center;">
|
||||
Rebuilt from the ground up to provide a deeper, more professional simulation environment for the UK Computing Curriculum.
|
||||
</p>
|
||||
<div class="divider"></div>
|
||||
<ul style="list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px;">
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> New User Interface (Responsive)</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Two's Complement Simulator</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Extended Binary Simulator (Custom sizes)</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Binary Simulator</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Extended Hexadecimal Simulator</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Hexadecimal Simulator</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Enhanced Gate Simulator (Truth Tables)</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Compound Gate Simulator</li>
|
||||
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Computer Components Simulator</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; align-items: center;">
|
||||
<div class="card">
|
||||
<h2 class="brandName" style="font-size: 24px; color: var(--accent);">From Bit:Box to Computing:Box</h2>
|
||||
<p style="color: var(--muted); line-height: 1.6;">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.</p>
|
||||
</div>
|
||||
<img src="/images/BitBoxLogo.png" alt="Bit:Box Logo" style="width: 100%; border-radius: 16px; border: 1px solid var(--line);" />
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; align-items: center;">
|
||||
<img src="/images/computingbox-concept-illustration.webp" alt="Simulator Illustration" style="width: 100%; border-radius: 16px; border: 1px solid var(--line);" />
|
||||
<div class="card">
|
||||
<h2 class="brandName" style="font-size: 24px; color: var(--accent);">Learning by Doing</h2>
|
||||
<p style="color: var(--muted); line-height: 1.6;">Students can interact with simulations, change values, and see results instantly. This helps them understand how concepts work rather than memorising rules.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center;">
|
||||
<div class="card">
|
||||
<h2 class="brandName" style="font-size: 24px; color: var(--accent);">Educational Impact</h2>
|
||||
<p style="color: var(--muted); line-height: 1.6;">Designed for use in lessons, independent study, and revision, helping students build confidence as they explore how computers work.</p>
|
||||
</div>
|
||||
<img src="/images/Educational_Impact.webp" alt="Classroom Impact" style="width: 100%; border-radius: 16px; border: 1px solid var(--line);" />
|
||||
</div>
|
||||
</article>
|
||||
</BaseLayout>
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
import "../styles/number-simulators.css";
|
||||
---
|
||||
|
||||
<BaseLayout title="Binary Simulator | Computing:Box">
|
||||
|
||||
28
src/pages/copyright.astro
Normal file
28
src/pages/copyright.astro
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
---
|
||||
|
||||
<BaseLayout title="Copyright Notice | Computing:Box">
|
||||
<div class="card" style="max-width: 800px; margin: 0 auto;">
|
||||
<h1 class="brandName" style="color: var(--accent); margin-bottom: 20px;">Copyright Notice</h1>
|
||||
<a href="https://www.computingbox.co.uk" style="color: var(--accent); text-decoration: none;">Computing:Box</a> © 2024 by <a href="https://www.mrlyall.uk/" style="color: var(--accent); text-decoration: none;">Mr A Lyall</a> is licensed under <strong><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" style="color: var(--accent); text-decoration: none;">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International</a></strong><img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;"><img src="https://mirrors.creativecommons.org/presskit/icons/nc.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;"><img src="https://mirrors.creativecommons.org/presskit/icons/sa.svg" alt="" style="max-width: 1em;max-height:1em;margin-left: .2em;">
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<h2 class="brandName" style="font-size: 20px;">What Does This Mean For You?</h2>
|
||||
<p style="color: var(--muted); font-size: 1.2em;">You are free to:</p>
|
||||
<ul style="color: var(--muted); line-height: 1.6; margin-bottom: 20px;">
|
||||
<li><strong>Share</strong> — copy and redistribute the material in any medium or format.</li>
|
||||
<li><strong>Adapt</strong> — remix, transform, and build upon the material.</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="brandName" style="font-size: 20px;">Under the following terms:</h2>
|
||||
<ul style="color: var(--muted); line-height: 1.6;">
|
||||
<li class="cc-by"><strong>Attribution </strong>— You must give <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en#ref-appropriate-credit" style="color: var(--accent); margin-bottom: 20px;">appropriate credit</a>, provide a link to the license, and <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en#ref-indicate-changes" style="color: var(--accent); margin-bottom: 20px;">indicate if changes were made</a>. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.</li>
|
||||
<li class="cc-nc"><strong>NonCommercial</strong> — You may not use the material for <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en#ref-commercial-purposes" style="color: var(--accent); margin-bottom: 20px;">commercial purposes</a>.</li>
|
||||
<li class="cc-sa"><strong>ShareAlike</strong> — If you remix, transform, or build upon the material, you must distribute your contributions under the <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en#ref-same-license" style="color: var(--accent); margin-bottom: 20px;">same license</a> as the original.</li>
|
||||
|
||||
<li><strong>No additional restrictions</strong> — You may not apply legal terms or <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en#ref-technological-measures" style="color: var(--accent); margin-bottom: 20px;">technological measures</a> that legally restrict others from doing anything the license permits.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
import "../styles/number-simulators.css";
|
||||
---
|
||||
|
||||
<BaseLayout title="Hex Colours Simulator | Computing:Box">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
import "../styles/number-simulators.css";
|
||||
---
|
||||
|
||||
<BaseLayout title="Hexadecimal Simulator | Computing:Box">
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
---
|
||||
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.
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Welcome />
|
||||
</Layout>
|
||||
<BaseLayout title="Welcome | Computing:Box">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; gap: 40px; min-height: 60vh; padding: 40px 0;">
|
||||
<div style="flex: 1;">
|
||||
<p style="color: var(--accent); font-weight: 800; letter-spacing: 2px; text-transform: uppercase; margin-bottom: 10px;">Version 2.0 Now Live</p>
|
||||
<h1 class="brandName" style="font-size: 48px; line-height: 1.1; margin-bottom: 24px;">Understand Computing concepts better.</h1>
|
||||
<p style="font-size: 18px; color: var(--muted);">
|
||||
Interactive simulators for Binary, Hexadecimal, Logic Gates, and Computer Components designed for the UK curriculum.
|
||||
</p>
|
||||
<div style="display: flex; gap: 16px; margin-top: 32px;">
|
||||
<a href="/about" class="btn btnAccent" style="text-decoration: none; padding: 14px 28px;">Learn More</a>
|
||||
<a href="/binary" class="btn" style="text-decoration: none; padding: 14px 28px;">Get Started</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; text-align: right;">
|
||||
<img src="/images/computing-box-logo.svg" alt="Computing Box Logo" style="width: 100%; max-width: 450px; filter: drop-shadow(0 0 50px rgba(40, 240, 122, 0.15));" />
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
20
src/pages/legal-code.astro
Normal file
20
src/pages/legal-code.astro
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
---
|
||||
|
||||
<BaseLayout title="Legal Code | Computing:Box">
|
||||
<div class="card" style="max-width: 900px; margin: 0 auto;">
|
||||
<h1 class="brandName" style="color: var(--accent); margin-bottom: 20px;">Legal Code</h1>
|
||||
|
||||
<div style="background: rgba(255, 193, 7, 0.1); border: 1px solid #ffc107; padding: 20px; border-radius: 12px; margin-bottom: 24px;">
|
||||
<h3 style="color: #ffc107; margin-top: 0; font-family: var(--ui-font);">About the license and Creative Commons</h3>
|
||||
<p style="color: #eee; margin-bottom: 0; font-size: 14px;">
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p style="color: var(--muted); font-size: 14px; line-height: 1.6;">
|
||||
By using this licensed material, you accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License.
|
||||
</p>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
@@ -4,58 +4,51 @@ import "../styles/logic-gates.css";
|
||||
---
|
||||
|
||||
<BaseLayout title="Logic Gate Builder | Computing:Box">
|
||||
<div class="binaryPage" id="logicPage">
|
||||
<div id="logicPage" class="lg-container">
|
||||
|
||||
<button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true">
|
||||
<span class="toolboxIcon" aria-hidden="true">🧰</span>
|
||||
<span class="toolboxText">TOOLBOX</span>
|
||||
</button>
|
||||
|
||||
<section class="topGrid" style="display:flex; flex-direction:column; height: 100%;">
|
||||
|
||||
<div class="readout">
|
||||
<div class="readoutBlock">
|
||||
<div class="label" style="margin-bottom: 8px;">Interactive Simulator</div>
|
||||
<div class="num denaryValue" style="font-size: 56px; line-height: 1;">LOGIC GATES</div>
|
||||
</div>
|
||||
<div style="color: var(--muted); font-size: 15px; font-family: var(--ui-font); text-align: center; max-width: 800px; margin-top: 12px;">
|
||||
Drag items from the toolbox to the board. Drag from output ports to input ports to wire. Click a wire or node and press <kbd style="background:rgba(255,255,255,0.1); padding:2px 6px; border-radius:4px; font-family:var(--ui-font);">Delete</kbd> to remove it.
|
||||
<div class="lg-top-header">
|
||||
<div class="lg-title">Interactive Logic Circuit Builder</div>
|
||||
<div class="lg-subtitle">
|
||||
Drag items from the toolbox. Drag from output ports to input ports to wire. Click a wire/node and press <kbd>Delete</kbd>. Scroll to Zoom.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="lg-workspace" id="workspace">
|
||||
|
||||
<div class="lg-zoom-controls">
|
||||
<button class="lg-zoom-btn" id="btnZoomIn" title="Zoom In">+</button>
|
||||
<button class="lg-zoom-btn" id="btnZoomOut" title="Zoom Out">−</button>
|
||||
<button class="lg-zoom-btn" id="btnZoomReset" title="Reset View" style="font-size: 16px;">⌂</button>
|
||||
</div>
|
||||
|
||||
<div class="lg-viewport" id="viewport">
|
||||
<svg class="lg-svg-layer" id="wireLayer"></svg>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<aside id="toolboxPanel" class="panelCol" aria-label="Toolbox">
|
||||
</div>
|
||||
|
||||
<aside id="toolboxPanel" class="lg-toolbox" aria-label="Toolbox">
|
||||
<div class="card">
|
||||
<div class="cardTitle">Components</div>
|
||||
|
||||
<div class="tb-icon-grid" id="toolboxGrid">
|
||||
<div class="tb-icon-grid" id="toolboxGrid"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">Live Truth Table</div>
|
||||
<details open>
|
||||
<summary class="tt-summary">Show / Hide Table</summary>
|
||||
<div style="font-family: var(--ui-font); font-size: 12px; color: var(--muted); margin-bottom: 12px;">Auto-generates based on current wiring. (Max 6 inputs)</div>
|
||||
<div class="tt-table-wrap" id="truthTableContainer">
|
||||
</div>
|
||||
<div class="tt-table-wrap" id="truthTableContainer"></div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">Tools</div>
|
||||
<button class="btn btnReset btnWide" id="btnClearBoard" type="button" style="margin-bottom:0;">Clear Board</button>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
|
||||
63
src/pages/pc-builder.astro
Normal file
63
src/pages/pc-builder.astro
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
import "../styles/pc-builder.css";
|
||||
---
|
||||
|
||||
<BaseLayout title="PC Part Simulator | Computing:Box">
|
||||
<div id="pcPage" class="pb-container">
|
||||
|
||||
<button id="toolboxToggle" class="toolboxToggle" type="button" aria-expanded="true">
|
||||
<span class="toolboxIcon" aria-hidden="true">🧰</span>
|
||||
<span class="toolboxText">TOOLBOX</span>
|
||||
</button>
|
||||
|
||||
<div class="pb-top-header">
|
||||
<div class="pb-title">PC Part Simulator</div>
|
||||
<div class="pb-subtitle">
|
||||
Build inside the Case! Snap the Motherboard into the chassis, then populate the slots. Add the side panel when done. <strong>Double-Click</strong> parts to inspect in 3D.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pb-workspace" id="workspace">
|
||||
<div class="pb-zoom-controls">
|
||||
<button class="pb-zoom-btn" id="btnZoomIn" title="Zoom In">+</button>
|
||||
<button class="pb-zoom-btn" id="btnZoomOut" title="Zoom Out">−</button>
|
||||
<button class="pb-zoom-btn" id="btnZoomReset" title="Reset View" style="font-size: 16px;">⌂</button>
|
||||
</div>
|
||||
|
||||
<div class="pb-viewport" id="viewport">
|
||||
<svg class="pb-svg-layer" id="wireLayer"></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside id="toolboxPanel" class="pb-toolbox" aria-label="Toolbox">
|
||||
<div class="card">
|
||||
<div class="cardTitle">Inventory</div>
|
||||
<div class="tb-icon-grid" id="toolboxGrid"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">System Diagnostics</div>
|
||||
<div style="font-family: var(--ui-font); font-size: 12px; color: var(--muted); margin-bottom: 12px;">Live pre-flight boot analysis.</div>
|
||||
<div class="specs-panel" id="buildSpecsContainer"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="cardTitle">Tools</div>
|
||||
<button class="btn btnReset btnWide" id="btnClearBoard" type="button" style="margin-bottom:0;">Disassemble PC</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div id="inspectModal" class="inspect-modal">
|
||||
<div class="inspect-close" id="inspectClose">×</div>
|
||||
<div id="inspectName" style="color: white; font-family: var(--ui-font); font-size: 28px; font-weight: 800; letter-spacing: 2px; text-transform: uppercase;"></div>
|
||||
<div class="inspect-stage" id="inspectStage">
|
||||
<div class="inspect-object" id="inspectObject"></div>
|
||||
</div>
|
||||
<div style="color: var(--muted); font-family: var(--ui-font); margin-top: 20px; font-size: 14px;">Move mouse to rotate component. Scroll to zoom.</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="../scripts/pcBuilder.js"></script>
|
||||
</BaseLayout>
|
||||
@@ -4,6 +4,7 @@
|
||||
(() => {
|
||||
/* --- DOM Elements --- */
|
||||
const workspace = document.getElementById("workspace");
|
||||
const viewport = document.getElementById("viewport");
|
||||
const wireLayer = document.getElementById("wireLayer");
|
||||
const ttContainer = document.getElementById("truthTableContainer");
|
||||
const toolboxGrid = document.getElementById("toolboxGrid");
|
||||
@@ -12,7 +13,7 @@
|
||||
const toolboxToggle = document.getElementById("toolboxToggle");
|
||||
const logicPage = document.getElementById("logicPage");
|
||||
|
||||
/* --- ANSI Gate SVGs (Strict 100x50 with built-in tails) --- */
|
||||
/* --- ANSI Gate SVGs --- */
|
||||
const GATE_SVGS = {
|
||||
'AND': `<g stroke="#e8e8ee" stroke-width="3" fill="none"><path d="M0,15 L20,15 M0,35 L20,35 M70,25 L100,25"/><path d="M20,5 L50,5 A20,20 0 0 1 50,45 L20,45 Z" fill="var(--bg)"/></g>`,
|
||||
'OR': `<g stroke="#e8e8ee" stroke-width="3" fill="none"><path d="M0,15 L26,15 M0,35 L26,35 M70,25 L100,25"/><path d="M20,5 Q55,5 70,25 Q55,45 20,45 Q40,25 20,5 Z" fill="var(--bg)"/></g>`,
|
||||
@@ -32,19 +33,22 @@
|
||||
|
||||
let nextNodeId = 1;
|
||||
let nextWireId = 1;
|
||||
let inputCount = 0;
|
||||
let outputCount = 0;
|
||||
|
||||
// Interaction State
|
||||
let isDraggingNode = null;
|
||||
let dragOffset = { x: 0, y: 0 };
|
||||
let clickStartX = 0, clickStartY = 0; // Fixes switch drag conflict
|
||||
let clickStartX = 0, clickStartY = 0;
|
||||
|
||||
let wiringStart = null;
|
||||
let tempWirePath = null;
|
||||
|
||||
let selectedWireId = null;
|
||||
let selectedNodeId = null;
|
||||
|
||||
// Camera State (Pan & Zoom)
|
||||
let panX = 0, panY = 0, zoom = 1;
|
||||
let isPanning = false;
|
||||
let panStart = { x: 0, y: 0 };
|
||||
|
||||
/* --- Setup Toolbox --- */
|
||||
function initToolbox() {
|
||||
if(!toolboxGrid) return;
|
||||
@@ -58,7 +62,6 @@
|
||||
<div class="tb-icon-label">Output</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Object.keys(GATE_SVGS).forEach(gate => {
|
||||
html += `
|
||||
<div draggable="true" data-spawn="GATE" data-gate="${gate}" class="drag-item tb-icon-box" title="${gate} Gate">
|
||||
@@ -67,7 +70,6 @@
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
toolboxGrid.innerHTML = html;
|
||||
|
||||
document.querySelectorAll('.drag-item').forEach(item => {
|
||||
@@ -78,7 +80,21 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Math & Geometry --- */
|
||||
/* --- Camera Math --- */
|
||||
function updateViewport() {
|
||||
viewport.style.transform = `translate(${panX}px, ${panY}px) scale(${zoom})`;
|
||||
workspace.style.backgroundSize = `${24 * zoom}px ${24 * zoom}px`;
|
||||
workspace.style.backgroundPosition = `${panX}px ${panY}px`;
|
||||
}
|
||||
|
||||
function zoomWorkspace(factor, mouseX, mouseY) {
|
||||
const newZoom = Math.min(Math.max(0.2, zoom * factor), 3);
|
||||
panX = mouseX - (mouseX - panX) * (newZoom / zoom);
|
||||
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};
|
||||
@@ -89,9 +105,10 @@
|
||||
const wsRect = workspace.getBoundingClientRect();
|
||||
const portRect = portEl.getBoundingClientRect();
|
||||
|
||||
// Calculate backwards through camera scale/pan to find true local coordinates
|
||||
return {
|
||||
x: portRect.left - wsRect.left + (portRect.width / 2),
|
||||
y: portRect.top - wsRect.top + (portRect.height / 2)
|
||||
x: (portRect.left - wsRect.left - panX + portRect.width / 2) / zoom,
|
||||
y: (portRect.top - wsRect.top - panY + portRect.height / 2) / zoom
|
||||
};
|
||||
}
|
||||
|
||||
@@ -103,37 +120,30 @@
|
||||
/* --- Rendering --- */
|
||||
function renderWires() {
|
||||
let svgHTML = '';
|
||||
|
||||
connections.forEach(conn => {
|
||||
const from = getPortCoords(conn.fromNode, 'out');
|
||||
const to = getPortCoords(conn.toNode, `in${conn.toPort}`);
|
||||
const sourceNode = nodes[conn.fromNode];
|
||||
const isActive = sourceNode && sourceNode.value === true;
|
||||
const isSelected = conn.id === selectedWireId;
|
||||
|
||||
svgHTML += `<path class="lg-wire ${isActive ? 'active' : ''} ${isSelected ? 'selected' : ''}" d="${drawBezier(from.x, from.y, to.x, to.y)}" data-conn-id="${conn.id}" />`;
|
||||
});
|
||||
|
||||
if (wiringStart && tempWirePath) {
|
||||
svgHTML += `<path class="lg-wire lg-wire-temp" d="${drawBezier(wiringStart.x, wiringStart.y, tempWirePath.x, tempWirePath.y)}" />`;
|
||||
}
|
||||
|
||||
wireLayer.innerHTML = svgHTML;
|
||||
}
|
||||
|
||||
function updateNodePositions() {
|
||||
Object.values(nodes).forEach(n => {
|
||||
if (n.el) {
|
||||
n.el.style.left = `${n.x}px`;
|
||||
n.el.style.top = `${n.y}px`;
|
||||
}
|
||||
if (n.el) { n.el.style.left = `${n.x}px`; n.el.style.top = `${n.y}px`; }
|
||||
});
|
||||
renderWires();
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedWireId = null;
|
||||
selectedNodeId = null;
|
||||
selectedWireId = null; selectedNodeId = null;
|
||||
document.querySelectorAll('.lg-node.selected').forEach(el => el.classList.remove('selected'));
|
||||
renderWires();
|
||||
}
|
||||
@@ -141,22 +151,16 @@
|
||||
/* --- Logic Evaluation --- */
|
||||
function evaluateGraph(overrideInputs = null) {
|
||||
let context = {};
|
||||
|
||||
Object.values(nodes).filter(n => n.type === 'INPUT').forEach(n => {
|
||||
context[n.id] = overrideInputs ? overrideInputs[n.id] : n.value;
|
||||
});
|
||||
|
||||
let changed = true;
|
||||
let loops = 0;
|
||||
|
||||
let changed = true; let loops = 0;
|
||||
while (changed && loops < 10) {
|
||||
changed = false;
|
||||
loops++;
|
||||
|
||||
changed = false; loops++;
|
||||
Object.values(nodes).filter(n => n.type === 'GATE').forEach(gate => {
|
||||
let in1Conn = connections.find(c => c.toNode === gate.id && c.toPort === '1');
|
||||
let in2Conn = connections.find(c => c.toNode === gate.id && c.toPort === '2');
|
||||
|
||||
let val1 = in1Conn ? (context[in1Conn.fromNode] || false) : false;
|
||||
let val2 = in2Conn ? (context[in2Conn.fromNode] || false) : false;
|
||||
|
||||
@@ -170,11 +174,7 @@
|
||||
case 'XOR': res = val1 !== val2; break;
|
||||
case 'XNOR': res = val1 === val2; break;
|
||||
}
|
||||
|
||||
if (context[gate.id] !== res) {
|
||||
context[gate.id] = res;
|
||||
changed = true;
|
||||
}
|
||||
if (context[gate.id] !== res) { context[gate.id] = res; changed = true; }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -195,7 +195,6 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return outStates;
|
||||
}
|
||||
|
||||
@@ -207,12 +206,10 @@
|
||||
const outNodes = Object.values(nodes).filter(n => n.type === 'OUTPUT').sort((a,b) => a.label.localeCompare(b.label));
|
||||
|
||||
if (inNodes.length === 0 || outNodes.length === 0) {
|
||||
ttContainer.innerHTML = '<div style="padding: 16px; color: var(--muted); text-align:center;">Add inputs and outputs to generate table.</div>';
|
||||
return;
|
||||
ttContainer.innerHTML = '<div style="padding: 16px; color: var(--muted); text-align:center;">Add inputs and outputs to generate table.</div>'; return;
|
||||
}
|
||||
if (inNodes.length > 6) {
|
||||
ttContainer.innerHTML = '<div style="padding: 16px; color: var(--muted); text-align:center;">Maximum 6 inputs supported.</div>';
|
||||
return;
|
||||
ttContainer.innerHTML = '<div style="padding: 16px; color: var(--muted); text-align:center;">Maximum 6 inputs supported.</div>'; return;
|
||||
}
|
||||
|
||||
let html = '<table class="tt-table"><thead><tr>';
|
||||
@@ -221,27 +218,16 @@
|
||||
html += '</tr></thead><tbody>';
|
||||
|
||||
const numRows = Math.pow(2, inNodes.length);
|
||||
|
||||
for (let i = 0; i < numRows; i++) {
|
||||
let override = {};
|
||||
inNodes.forEach((n, idx) => {
|
||||
override[n.id] = ((i >> (inNodes.length - 1 - idx)) & 1) === 1;
|
||||
});
|
||||
|
||||
inNodes.forEach((n, idx) => { override[n.id] = ((i >> (inNodes.length - 1 - idx)) & 1) === 1; });
|
||||
let outStates = evaluateGraph(override);
|
||||
|
||||
html += '<tr>';
|
||||
inNodes.forEach(n => {
|
||||
let val = override[n.id];
|
||||
html += `<td class="${val ? 'tt-on' : ''}">${val ? 1 : 0}</td>`;
|
||||
});
|
||||
outNodes.forEach(n => {
|
||||
let val = outStates[n.id];
|
||||
html += `<td class="${val ? 'tt-on' : ''}" style="font-weight:bold;">${val ? 1 : 0}</td>`;
|
||||
});
|
||||
inNodes.forEach(n => { let val = override[n.id]; html += `<td class="${val ? 'tt-on' : ''}">${val ? 1 : 0}</td>`; });
|
||||
outNodes.forEach(n => { let val = outStates[n.id]; html += `<td class="${val ? 'tt-on' : ''}" style="font-weight:bold;">${val ? 1 : 0}</td>`; });
|
||||
html += '</tr>';
|
||||
}
|
||||
|
||||
html += '</tbody></table>';
|
||||
ttContainer.innerHTML = html;
|
||||
}
|
||||
@@ -252,13 +238,24 @@
|
||||
generateTruthTable();
|
||||
}
|
||||
|
||||
/* --- Smart Label Generation --- */
|
||||
function getNextInputLabel() {
|
||||
let charCode = 65;
|
||||
while (Object.values(nodes).some(n => n.type === 'INPUT' && n.label === String.fromCharCode(charCode))) { charCode++; }
|
||||
return String.fromCharCode(charCode);
|
||||
}
|
||||
|
||||
function getNextOutputLabel() {
|
||||
let idx = 1;
|
||||
while (Object.values(nodes).some(n => n.type === 'OUTPUT' && n.label === ('Q' + idx))) { idx++; }
|
||||
return 'Q' + idx;
|
||||
}
|
||||
|
||||
/* --- Node Creation --- */
|
||||
function createNodeElement(node) {
|
||||
const el = document.createElement('div');
|
||||
el.className = `lg-node`;
|
||||
el.dataset.id = node.id;
|
||||
el.style.left = `${node.x}px`;
|
||||
el.style.top = `${node.y}px`;
|
||||
el.className = `lg-node`; el.dataset.id = node.id;
|
||||
el.style.left = `${node.x}px`; el.style.top = `${node.y}px`;
|
||||
|
||||
let innerHTML = `<div class="lg-header">${node.label}</div><div class="lg-gate-container">`;
|
||||
|
||||
@@ -266,12 +263,12 @@
|
||||
innerHTML += `
|
||||
<div class="switch" style="margin:0;"><span class="slider"></span></div>
|
||||
${INPUT_SVG}
|
||||
<div class="lg-port port-out" data-port="out" style="top: 25px; left: 86px;"></div>
|
||||
<div class="lg-port" data-port="out" style="top: 25px; left: 86px;"></div>
|
||||
`;
|
||||
}
|
||||
else if (node.type === 'OUTPUT') {
|
||||
innerHTML += `
|
||||
<div class="lg-port port-in-1" data-port="in1" style="top: 25px; left: 0;"></div>
|
||||
<div class="lg-port" data-port="in1" style="top: 25px; left: 0;"></div>
|
||||
${OUTPUT_SVG}
|
||||
<div class="bulb" style="margin:0;"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2.25a6.75 6.75 0 0 0-6.75 6.75c0 2.537 1.393 4.75 3.493 5.922l.507.282v1.546h5.5v-1.546l.507-.282A6.75 6.75 0 0 0 12 2.25Zm-2.25 16.5v.75a2.25 2.25 0 0 0 4.5 0v-.75h-4.5Z"/></svg></div>
|
||||
`;
|
||||
@@ -279,46 +276,43 @@
|
||||
else if (node.type === 'GATE') {
|
||||
const isNot = node.gateType === 'NOT';
|
||||
innerHTML += `
|
||||
<div class="lg-port port-in-1" data-port="in1" style="top: ${isNot ? '25px' : '15px'}; left: 0;"></div>
|
||||
${!isNot ? `<div class="lg-port port-in-2" data-port="in2" style="top: 35px; left: 0;"></div>` : ''}
|
||||
<div class="lg-port" data-port="in1" style="top: ${isNot ? '25px' : '15px'}; left: 0;"></div>
|
||||
${!isNot ? `<div class="lg-port" data-port="in2" style="top: 35px; left: 0;"></div>` : ''}
|
||||
<svg class="lg-gate-svg" viewBox="0 0 100 50">${GATE_SVGS[node.gateType]}</svg>
|
||||
<div class="lg-port port-out" data-port="out" style="top: 25px; left: 100px;"></div>
|
||||
<div class="lg-port" data-port="out" style="top: 25px; left: 100px;"></div>
|
||||
`;
|
||||
}
|
||||
|
||||
innerHTML += `</div>`;
|
||||
el.innerHTML = innerHTML;
|
||||
workspace.appendChild(el);
|
||||
|
||||
viewport.appendChild(el);
|
||||
node.el = el;
|
||||
|
||||
if (node.type === 'INPUT') {
|
||||
// Custom click handler to prevent dragging from toggling the switch
|
||||
el.querySelector('.switch').addEventListener('click', (e) => {
|
||||
const dist = Math.hypot(e.clientX - clickStartX, e.clientY - clickStartY);
|
||||
if (isDraggingNode || dist > 3) {
|
||||
e.preventDefault();
|
||||
if (dist > 3) {
|
||||
e.preventDefault(); // Prevents toggle if it was a drag motion
|
||||
} else {
|
||||
node.value = !node.value;
|
||||
el.querySelector('.switch').classList.toggle('active-sim', node.value);
|
||||
el.querySelector('.slider').style.background = node.value ? 'rgba(40,240,122,.25)' : '';
|
||||
el.querySelector('.slider').style.borderColor = node.value ? 'rgba(40,240,122,.30)' : '';
|
||||
el.querySelector('.slider').innerHTML = node.value ? `<style>#${node.id} .slider::before { transform: translateX(28px); }</style>` : '';
|
||||
el.querySelector('.slider').innerHTML = node.value ? `<style>#logicPage [data-id="${node.id}"] .slider::before { transform: translateX(28px); }</style>` : '';
|
||||
runSimulation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function spawnNode(type, gateType = null, dropX = null, dropY = null) {
|
||||
let label = '';
|
||||
if (type === 'INPUT') { inputCount++; label = String.fromCharCode(64 + inputCount); }
|
||||
if (type === 'OUTPUT') { outputCount++; label = `Q${outputCount}`; }
|
||||
if (type === 'GATE') { label = gateType; }
|
||||
if (type === 'INPUT') label = getNextInputLabel();
|
||||
if (type === 'OUTPUT') label = getNextOutputLabel();
|
||||
if (type === 'GATE') label = gateType;
|
||||
|
||||
const id = `node_${nextNodeId++}`;
|
||||
|
||||
const offset = Math.floor(Math.random() * 40);
|
||||
const x = dropX !== null ? dropX : (type === 'INPUT' ? 50 : (type === 'OUTPUT' ? 600 : 300) + offset);
|
||||
const y = dropY !== null ? dropY : 150 + offset;
|
||||
@@ -331,9 +325,26 @@
|
||||
|
||||
/* --- Global Interaction Handlers --- */
|
||||
|
||||
// Camera Zoom Controls
|
||||
document.getElementById("btnZoomIn")?.addEventListener('click', () => {
|
||||
const r = workspace.getBoundingClientRect(); zoomWorkspace(1.2, r.width/2, r.height/2);
|
||||
});
|
||||
document.getElementById("btnZoomOut")?.addEventListener('click', () => {
|
||||
const r = workspace.getBoundingClientRect(); zoomWorkspace(1/1.2, r.width/2, r.height/2);
|
||||
});
|
||||
document.getElementById("btnZoomReset")?.addEventListener('click', () => {
|
||||
panX = 0; panY = 0; zoom = 1; updateViewport();
|
||||
});
|
||||
|
||||
workspace.addEventListener('wheel', (e) => {
|
||||
e.preventDefault();
|
||||
const wsRect = workspace.getBoundingClientRect();
|
||||
const factor = e.deltaY < 0 ? 1.1 : (1/1.1);
|
||||
zoomWorkspace(factor, e.clientX - wsRect.left, e.clientY - wsRect.top);
|
||||
});
|
||||
|
||||
workspace.addEventListener('mousedown', (e) => {
|
||||
clickStartX = e.clientX;
|
||||
clickStartY = e.clientY;
|
||||
clickStartX = e.clientX; clickStartY = e.clientY;
|
||||
|
||||
const port = e.target.closest('.lg-port');
|
||||
if (port) {
|
||||
@@ -342,11 +353,7 @@
|
||||
|
||||
if (portId.startsWith('in')) {
|
||||
const existingIdx = connections.findIndex(c => c.toNode === nodeEl.dataset.id && c.toPort === portId.replace('in', ''));
|
||||
if (existingIdx !== -1) {
|
||||
connections.splice(existingIdx, 1);
|
||||
runSimulation();
|
||||
return;
|
||||
}
|
||||
if (existingIdx !== -1) { connections.splice(existingIdx, 1); runSimulation(); return; }
|
||||
}
|
||||
|
||||
if (portId === 'out') {
|
||||
@@ -371,32 +378,41 @@
|
||||
clearSelection();
|
||||
selectedNodeId = nodeEl.dataset.id;
|
||||
nodeEl.classList.add('selected');
|
||||
|
||||
isDraggingNode = nodeEl.dataset.id;
|
||||
|
||||
const rect = nodeEl.getBoundingClientRect();
|
||||
dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
|
||||
dragOffset = { x: (e.clientX - rect.left) / zoom, y: (e.clientY - rect.top) / zoom };
|
||||
return;
|
||||
}
|
||||
|
||||
// Clicked empty space -> Pan Camera
|
||||
clearSelection();
|
||||
isPanning = true;
|
||||
panStart = { x: e.clientX - panX, y: e.clientY - panY };
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
const wsRect = workspace.getBoundingClientRect();
|
||||
|
||||
if (isPanning) {
|
||||
panX = e.clientX - panStart.x;
|
||||
panY = e.clientY - panStart.y;
|
||||
updateViewport();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDraggingNode) {
|
||||
const node = nodes[isDraggingNode];
|
||||
let newX = e.clientX - wsRect.left - dragOffset.x;
|
||||
let newY = e.clientY - wsRect.top - dragOffset.y;
|
||||
node.x = Math.max(10, Math.min(newX, wsRect.width - 80));
|
||||
node.y = Math.max(20, Math.min(newY, wsRect.height - 60));
|
||||
let newX = (e.clientX - wsRect.left - panX) / zoom - dragOffset.x;
|
||||
let newY = (e.clientY - wsRect.top - panY) / zoom - dragOffset.y;
|
||||
node.x = newX; node.y = newY;
|
||||
updateNodePositions();
|
||||
}
|
||||
|
||||
if (wiringStart) {
|
||||
tempWirePath = {
|
||||
x: e.clientX - wsRect.left,
|
||||
y: e.clientY - wsRect.top
|
||||
x: (e.clientX - wsRect.left - panX) / zoom,
|
||||
y: (e.clientY - wsRect.top - panY) / zoom
|
||||
};
|
||||
renderWires();
|
||||
}
|
||||
@@ -404,6 +420,7 @@
|
||||
|
||||
window.addEventListener('mouseup', (e) => {
|
||||
isDraggingNode = null;
|
||||
isPanning = false;
|
||||
|
||||
if (wiringStart) {
|
||||
const port = e.target.closest('.lg-port');
|
||||
@@ -413,18 +430,10 @@
|
||||
|
||||
if (targetNodeId !== wiringStart.node) {
|
||||
connections = connections.filter(c => !(c.toNode === targetNodeId && c.toPort === targetPortId));
|
||||
|
||||
connections.push({
|
||||
id: `conn_${nextWireId++}`,
|
||||
fromNode: wiringStart.node,
|
||||
fromPort: 'out',
|
||||
toNode: targetNodeId,
|
||||
toPort: targetPortId
|
||||
});
|
||||
connections.push({ id: `conn_${nextWireId++}`, fromNode: wiringStart.node, fromPort: 'out', toNode: targetNodeId, toPort: targetPortId });
|
||||
}
|
||||
}
|
||||
wiringStart = null;
|
||||
tempWirePath = null;
|
||||
wiringStart = null; tempWirePath = null;
|
||||
runSimulation();
|
||||
}
|
||||
});
|
||||
@@ -434,17 +443,15 @@
|
||||
if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||
if (selectedWireId) {
|
||||
connections = connections.filter(c => c.id !== selectedWireId);
|
||||
clearSelection();
|
||||
runSimulation();
|
||||
clearSelection(); runSimulation();
|
||||
}
|
||||
else if (selectedNodeId) {
|
||||
connections = connections.filter(c => c.fromNode !== selectedNodeId && c.toNode !== selectedNodeId);
|
||||
if (nodes[selectedNodeId] && nodes[selectedNodeId].el) {
|
||||
workspace.removeChild(nodes[selectedNodeId].el);
|
||||
viewport.removeChild(nodes[selectedNodeId].el);
|
||||
}
|
||||
delete nodes[selectedNodeId];
|
||||
clearSelection();
|
||||
runSimulation();
|
||||
clearSelection(); runSimulation();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -457,19 +464,16 @@
|
||||
if (spawnType) {
|
||||
const gateType = e.dataTransfer.getData('gateType');
|
||||
const wsRect = workspace.getBoundingClientRect();
|
||||
const x = e.clientX - wsRect.left - 40;
|
||||
const y = e.clientY - wsRect.top - 30;
|
||||
const x = (e.clientX - wsRect.left - panX) / zoom - 40;
|
||||
const y = (e.clientY - wsRect.top - panY) / zoom - 30;
|
||||
spawnNode(spawnType, gateType || null, x, y);
|
||||
}
|
||||
});
|
||||
|
||||
/* --- Init --- */
|
||||
btnClearBoard?.addEventListener('click', () => {
|
||||
workspace.querySelectorAll('.lg-node').forEach(el => el.remove());
|
||||
nodes = {};
|
||||
connections = [];
|
||||
inputCount = 0;
|
||||
outputCount = 0;
|
||||
viewport.querySelectorAll('.lg-node').forEach(el => el.remove());
|
||||
nodes = {}; connections = [];
|
||||
runSimulation();
|
||||
});
|
||||
|
||||
@@ -481,9 +485,4 @@
|
||||
});
|
||||
|
||||
initToolbox();
|
||||
spawnNode('INPUT', null, 80, 150);
|
||||
spawnNode('INPUT', null, 80, 250);
|
||||
spawnNode('GATE', 'AND', 320, 200);
|
||||
spawnNode('OUTPUT', null, 600, 200);
|
||||
|
||||
})();
|
||||
423
src/scripts/pcBuilder.js
Normal file
423
src/scripts/pcBuilder.js
Normal file
@@ -0,0 +1,423 @@
|
||||
// src/scripts/pcBuilder.js
|
||||
// Computing:Box — Advanced PC Sandbox
|
||||
|
||||
(() => {
|
||||
const workspace = document.getElementById("workspace");
|
||||
const viewport = document.getElementById("viewport");
|
||||
const wireLayer = document.getElementById("wireLayer");
|
||||
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");
|
||||
|
||||
/* --- Extensive PC Component Library --- */
|
||||
const PC_PARTS = {
|
||||
'CASE': {
|
||||
name: 'ATX PC Case', w: 600, h: 550, z: 5, ports: [],
|
||||
slots: {
|
||||
'MB1': { x: 20, y: 20, accepts: 'MB' },
|
||||
'PSU1': { x: 20, y: 440, accepts: 'PSU' },
|
||||
'HDD1': { x: 440, y: 20, accepts: 'HDD' },
|
||||
'HDD2': { x: 440, y: 170, accepts: 'HDD' },
|
||||
'SATA_SSD1': { x: 440, y: 320, accepts: 'SATA_SSD' },
|
||||
'SATA_SSD2': { x: 440, y: 400, accepts: 'SATA_SSD' }
|
||||
},
|
||||
svg: `<rect width="600" height="550" fill="#15171c" rx="10" stroke="#333" stroke-width="4"/><rect x="20" y="20" width="380" height="400" fill="none" stroke="#222" stroke-width="2"/><rect x="20" y="440" width="180" height="90" fill="none" stroke="#222" stroke-width="2"/><rect x="440" y="20" width="140" height="510" fill="none" stroke="#222" stroke-width="2"/>`
|
||||
},
|
||||
'MB': {
|
||||
name: 'Motherboard', w: 360, h: 400, z: 10,
|
||||
ports: [
|
||||
{ id: 'atx_pwr', x: 340, y: 150 }, { id: 'sata1', x: 340, y: 300 }, { id: 'sata2', x: 340, y: 330 },
|
||||
{ id: 'usb1', x: 10, y: 40 }, { id: 'usb2', x: 10, y: 70 }, { id: 'usb3', x: 10, y: 100 }, { id: 'usb4', x: 10, y: 130 },
|
||||
{ id: 'audio', x: 10, y: 170 }, { id: 'disp', x: 10, y: 210 }
|
||||
],
|
||||
slots: {
|
||||
'CPU1': { x: 120, y: 40, accepts: 'CPU' },
|
||||
'COOLER1': { x: 100, y: 20, accepts: 'COOLER' },
|
||||
'RAM1': { x: 230, y: 30, accepts: 'RAM' }, 'RAM2': { x: 250, y: 30, accepts: 'RAM' },
|
||||
'RAM3': { x: 270, y: 30, accepts: 'RAM' }, 'RAM4': { x: 290, y: 30, accepts: 'RAM' },
|
||||
'M2_1': { x: 120, y: 170, accepts: 'M2_SSD' }, 'M2_2': { x: 120, y: 250, accepts: 'M2_SSD' },
|
||||
'PCIE1': { x: 40, y: 200, accepts: 'GPU' }, 'PCIE2': { x: 40, y: 300, accepts: 'GPU' }
|
||||
},
|
||||
// Uses a lighter slate grey #2C303A to stand out from the case
|
||||
svg: `<rect width="360" height="400" fill="#2C303A" rx="8" stroke="#4b5060" stroke-width="3"/><rect x="120" y="40" width="80" height="80" fill="#1f2229" stroke="#4b5060"/><rect x="230" y="30" width="15" height="100" fill="#1f2229"/><rect x="250" y="30" width="15" height="100" fill="#1f2229"/><rect x="270" y="30" width="15" height="100" fill="#1f2229"/><rect x="290" y="30" width="15" height="100" fill="#1f2229"/><rect x="40" y="200" width="280" height="15" fill="#15171c"/><rect x="40" y="300" width="280" height="15" fill="#15171c"/><rect x="120" y="170" width="80" height="15" fill="#1f2229"/><rect x="120" y="250" width="80" height="15" fill="#1f2229"/>`
|
||||
},
|
||||
'CPU': { name: 'Processor', w: 80, h: 80, z: 20, ports: [], slots: {}, svg: `<rect width="80" height="80" fill="#0b381a"/><rect x="10" y="10" width="60" height="60" rx="4" fill="#d4d4d4"/><polygon points="5,75 15,75 5,65" fill="#ffd700"/><text x="40" y="45" fill="#555" font-family="sans-serif" font-size="14" font-weight="bold" text-anchor="middle">CPU</text>` },
|
||||
'COOLER': { name: 'CPU Fan', w: 120, h: 120, z: 30, ports: [], slots: {}, svg: `<rect width="120" height="120" rx="60" fill="#1a1c23" stroke="#aaa" stroke-width="3"/><circle cx="60" cy="60" r="50" fill="#111"/><path d="M60,15 A45,45 0 0,1 105,60 L60,60 Z" fill="#444"/><path d="M105,60 A45,45 0 0,1 60,105 L60,60 Z" fill="#555"/><path d="M60,105 A45,45 0 0,1 15,60 L60,60 Z" fill="#444"/><path d="M15,60 A45,45 0 0,1 60,15 L60,60 Z" fill="#555"/><circle cx="60" cy="60" r="20" fill="#222"/>` },
|
||||
'RAM': { name: 'DDR4 Memory', w: 15, h: 100, z: 20, ports: [], slots: {}, svg: `<rect width="15" height="100" fill="#111"/><rect x="2" y="5" width="11" height="80" fill="#2a2a2a"/><rect x="0" y="90" width="15" height="10" fill="#ffd700"/>` },
|
||||
'GPU': { name: 'Graphics Card', w: 280, h: 60, z: 40, slots: {}, ports: [{ id: 'pwr_in', x: 270, y: 10 }, { id: 'disp_out', x: 10, y: 30 }], svg: `<rect width="280" height="60" rx="5" fill="#1a1a1a"/><circle cx="70" cy="30" r="22" fill="#111" stroke="#333" stroke-width="2"/><circle cx="140" cy="30" r="22" fill="#111" stroke="#333" stroke-width="2"/><circle cx="210" cy="30" r="22" fill="#111" stroke="#333" stroke-width="2"/><rect x="20" y="55" width="80" height="5" fill="#ffd700"/>` },
|
||||
'M2_SSD': { name: 'M.2 NVMe SSD', w: 80, h: 15, z: 20, ports: [], slots: {}, svg: `<rect width="80" height="15" rx="1" fill="#000"/><rect x="10" y="2" width="20" height="11" fill="#1a1a1a"/><rect x="35" y="2" width="20" height="11" fill="#1a1a1a"/><rect x="60" y="2" width="10" height="11" fill="#ccc"/><rect x="0" y="0" width="4" height="15" fill="#ffd700"/>` },
|
||||
'SATA_SSD': { name: '2.5" SATA SSD', w: 100, h: 70, z: 20, slots: {}, ports: [{id:'data', x:90, y:20}, {id:'pwr', x:90, y:50}], svg: `<rect width="100" height="70" fill="#111" rx="4" stroke="#444"/><rect x="10" y="10" width="80" height="50" fill="#1a1a1a" rx="2" stroke="#222"/><text x="50" y="40" fill="#888" font-family="sans-serif" font-size="14" font-weight="bold" text-anchor="middle">SSD</text>` },
|
||||
'HDD': { name: '3.5" Mech HDD', w: 120, h: 140, z: 20, slots: {}, ports: [{id:'data', x:110, y:20}, {id:'pwr', x:110, y:120}], svg: `<rect width="120" height="140" fill="#d0d0d0" rx="4" stroke="#888"/><rect x="10" y="10" width="100" height="100" fill="#e0e0e0" rx="50"/><circle cx="60" cy="60" r="35" fill="#ddd" stroke="#aaa"/><circle cx="60" cy="60" r="10" fill="#999"/><rect x="30" y="120" width="60" height="10" fill="#111"/>` },
|
||||
'PSU': { name: 'Power Supply', w: 160, h: 90, z: 20, slots: {}, ports: [{id:'out1',x:150,y:20}, {id:'out2',x:150,y:40}, {id:'out3',x:150,y:60}, {id:'out4',x:150,y:80}], svg: `<rect width="160" height="90" rx="4" fill="#1a1a1a" stroke="#333" stroke-width="2"/><circle cx="80" cy="45" r="35" fill="#0a0a0a" stroke="#222" stroke-width="2"/><line x1="80" y1="10" x2="80" y2="80" stroke="#333" stroke-width="2"/><line x1="45" y1="45" x2="115" y2="45" stroke="#333" stroke-width="2"/><circle cx="80" cy="45" r="10" fill="#222"/>` },
|
||||
'MONITOR': { name: 'Monitor', w: 240, h: 160, z: 30, slots: {}, ports: [{id:'disp', x:120, y:140}], svg: `<rect width="240" height="160" fill="#111" rx="5"/><rect x="10" y="10" width="220" height="120" fill="#000"/><rect x="100" y="140" width="40" height="20" fill="#222"/><rect x="60" y="150" width="120" height="10" fill="#222"/>` },
|
||||
'KEYBOARD': { name: 'Keyboard', w: 180, h: 60, z: 30, slots: {}, ports: [{id:'usb', x:90, y:10}], svg: `<rect width="180" height="60" fill="#111" rx="3"/><rect x="5" y="5" width="170" height="50" fill="#222" rx="2" stroke="#333" stroke-dasharray="8 8"/>` },
|
||||
'MOUSE': { name: 'Mouse', w: 30, h: 50, z: 30, slots: {}, ports: [{id:'usb', x:15, y:5}], svg: `<rect width="30" height="50" fill="#111" rx="15"/><line x1="15" y1="0" x2="15" y2="20" stroke="#333" stroke-width="2"/><circle cx="15" cy="15" r="4" fill="#333"/>` },
|
||||
'SPEAKER': { name: 'Speakers', w: 40, h: 80, z: 30, slots: {}, ports: [{id:'audio', x:20, y:10}], svg: `<rect width="40" height="80" fill="#111" rx="4"/><circle cx="20" cy="25" r="12" fill="#222"/><circle cx="20" cy="60" r="16" fill="#222"/>` }
|
||||
};
|
||||
|
||||
let nodes = {};
|
||||
let connections = [];
|
||||
let nextNodeId = 1, nextWireId = 1;
|
||||
|
||||
let isDraggingNode = null, dragOffset = { x: 0, y: 0 };
|
||||
let wiringStart = null, tempWirePath = null;
|
||||
let selectedWireId = null, selectedNodeId = null;
|
||||
|
||||
let panX = 0, panY = 0, zoom = 1;
|
||||
let isPanning = false, panStart = { x: 0, y: 0 };
|
||||
|
||||
/* --- Setup Toolbox --- */
|
||||
function initToolbox() {
|
||||
if(!toolboxGrid) return;
|
||||
let html = '';
|
||||
Object.keys(PC_PARTS).forEach(partKey => {
|
||||
html += `
|
||||
<div draggable="true" data-spawn="${partKey}" class="drag-item tb-icon-box" title="${PC_PARTS[partKey].name}">
|
||||
<svg viewBox="0 0 ${PC_PARTS[partKey].w} ${PC_PARTS[partKey].h}" style="max-width:80%; max-height:40px; pointer-events:none;">${PC_PARTS[partKey].svg}</svg>
|
||||
<div class="tb-icon-label">${partKey}</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
toolboxGrid.innerHTML = html;
|
||||
document.querySelectorAll('.drag-item').forEach(item => {
|
||||
item.addEventListener('dragstart', (e) => { e.dataTransfer.setData('spawnType', item.dataset.spawn); });
|
||||
});
|
||||
}
|
||||
|
||||
/* --- Camera Math --- */
|
||||
function updateViewport() {
|
||||
viewport.style.transform = `translate(${panX}px, ${panY}px) scale(${zoom})`;
|
||||
workspace.style.backgroundSize = `${32 * zoom}px ${32 * zoom}px`;
|
||||
workspace.style.backgroundPosition = `${panX}px ${panY}px`;
|
||||
}
|
||||
function zoomWorkspace(factor, mouseX, mouseY) {
|
||||
const newZoom = Math.min(Math.max(0.1, zoom * factor), 2);
|
||||
panX = mouseX - (mouseX - panX) * (newZoom / zoom);
|
||||
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 drawBezier(x1, y1, x2, y2) {
|
||||
const cpDist = Math.abs(x2 - x1) * 0.6 + 20;
|
||||
return `M ${x1} ${y1} C ${x1 + cpDist} ${y1}, ${x2 - cpDist} ${y2}, ${x2} ${y2}`;
|
||||
}
|
||||
|
||||
/* --- 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 += `<path class="pb-wire active ${isSelected ? 'selected' : ''}" d="${drawBezier(from.x, from.y, to.x, to.y)}" data-conn-id="${conn.id}" />`;
|
||||
});
|
||||
if (wiringStart && tempWirePath) {
|
||||
svgHTML += `<path class="pb-wire pb-wire-temp" d="${drawBezier(wiringStart.x, wiringStart.y, tempWirePath.x, tempWirePath.y)}" />`;
|
||||
}
|
||||
wireLayer.innerHTML = svgHTML;
|
||||
}
|
||||
|
||||
function updateNodePositions() {
|
||||
Object.values(nodes).forEach(n => {
|
||||
if (n.el) { n.el.style.left = `${n.x}px`; n.el.style.top = `${n.y}px`; }
|
||||
});
|
||||
renderWires();
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectedWireId = null; selectedNodeId = null;
|
||||
document.querySelectorAll('.pb-node.selected').forEach(el => el.classList.remove('selected'));
|
||||
renderWires();
|
||||
}
|
||||
|
||||
/* --- 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 = `
|
||||
<div class="diag-cat">Core System</div>
|
||||
<div class="diag-row"><span>CHASSIS</span><span style="color: ${hasCase ? '#28f07a' : '#ff5555'}">${hasCase ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>MOTHERBOARD</span><span style="color: ${hasMB ? '#28f07a' : '#ff5555'}">${hasMB ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>CPU</span><span style="color: ${hasCPU ? '#28f07a' : '#ff5555'}">${hasCPU ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>COOLING</span><span style="color: ${hasCooler ? '#28f07a' : '#ff5555'}">${hasCooler ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>MEMORY</span><span style="color: ${hasRAM ? '#28f07a' : '#ff5555'}">${hasRAM ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>POWER SPLY</span><span style="color: ${hasPSU ? '#28f07a' : '#ff5555'}">${hasPSU ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-cat">Connections</div>
|
||||
<div class="diag-row"><span>MB POWER</span><span style="color: ${mbPwr ? '#28f07a' : '#ff5555'}">${mbPwr ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>STORAGE</span><span style="color: ${hasStorage ? '#28f07a' : '#ff5555'}">${hasStorage ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>GPU POWER</span><span style="color: ${!hasGPU ? '#888' : (gpuPwr ? '#28f07a' : '#ff5555')}">${!hasGPU ? 'N/A' : (gpuPwr ? 'OK' : 'ERR')}</span></div>
|
||||
<div class="diag-row"><span>DISPLAY</span><span style="color: ${dispConn ? '#28f07a' : '#ff5555'}">${dispConn ? 'OK' : 'ERR'}</span></div>
|
||||
<div class="diag-row"><span>USB DEVS</span><span style="color: #55aaff">${usbCount}</span></div>
|
||||
<hr style="border-color: rgba(255,255,255,0.1); margin: 12px 0 8px 0;">
|
||||
<div style="text-align:center; font-size: 28px; color: ${isBootable ? '#28f07a' : '#ff5555'}; font-family: var(--bit-font); letter-spacing: 2px;">
|
||||
${isBootable ? 'BOOTING...' : 'HALTED'}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/* --- Node Creation & Snapping --- */
|
||||
function createNodeElement(node) {
|
||||
const el = document.createElement('div');
|
||||
el.className = `pb-node`; el.dataset.id = node.id;
|
||||
el.style.left = `${node.x}px`; el.style.top = `${node.y}px`;
|
||||
el.style.width = `${PC_PARTS[node.type].w}px`; el.style.height = `${PC_PARTS[node.type].h}px`;
|
||||
el.style.zIndex = PC_PARTS[node.type].z;
|
||||
|
||||
let innerHTML = `<svg class="pb-part-svg" viewBox="0 0 ${PC_PARTS[node.type].w} ${PC_PARTS[node.type].h}">${PC_PARTS[node.type].svg}</svg>`;
|
||||
PC_PARTS[node.type].ports.forEach(p => {
|
||||
innerHTML += `<div class="pb-port" data-port="${p.id}" style="left: ${p.x}px; top: ${p.y}px;"></div>`;
|
||||
});
|
||||
|
||||
// Debug Labels for bare parts
|
||||
if(node.type !== 'CASE' && node.type !== 'MB') {
|
||||
innerHTML += `<div style="position:absolute; top:-20px; font-family:var(--ui-font); font-size:12px; color:var(--muted);">${node.type}</div>`;
|
||||
}
|
||||
|
||||
el.innerHTML = innerHTML;
|
||||
viewport.appendChild(el);
|
||||
node.el = el;
|
||||
return el;
|
||||
}
|
||||
|
||||
function spawnNode(type, dropX = null, dropY = null) {
|
||||
const id = `node_${nextNodeId++}`;
|
||||
const x = dropX !== null ? dropX : 300 + Math.random()*40;
|
||||
const y = dropY !== null ? dropY : 150 + Math.random()*40;
|
||||
|
||||
const node = { id, type, x, y, snappedTo: null, el: null };
|
||||
if (PC_PARTS[type].slots) node.slots = { ...PC_PARTS[type].slots }; // Copy slots schema, values will be filled with IDs
|
||||
|
||||
// Reset slot values to null
|
||||
if(node.slots) {
|
||||
for(let k in node.slots) { node.slots[k] = null; }
|
||||
}
|
||||
|
||||
nodes[id] = node;
|
||||
createNodeElement(node);
|
||||
evaluateBuild();
|
||||
}
|
||||
|
||||
// Recursive movement to handle nested snaps (MB inside CASE inside ...)
|
||||
function moveNodeRecursive(nodeId, dx, dy) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Inspect Mode --- */
|
||||
let inspectZoom = 1, inspectRotX = 0, inspectRotY = 0;
|
||||
workspace.addEventListener('dblclick', (e) => {
|
||||
const nodeEl = e.target.closest('.pb-node');
|
||||
if (nodeEl) {
|
||||
const node = nodes[nodeEl.dataset.id];
|
||||
document.getElementById('inspectModal').classList.add('active');
|
||||
document.getElementById('inspectObject').innerHTML = `<svg viewBox="0 0 ${PC_PARTS[node.type].w} ${PC_PARTS[node.type].h}" style="width:100%; height:100%;">${PC_PARTS[node.type].svg}</svg>`;
|
||||
document.getElementById('inspectName').innerText = PC_PARTS[node.type].name;
|
||||
inspectZoom = 1.5; inspectRotX = 0; inspectRotY = 0; updateInspectTransform(); clearSelection();
|
||||
}
|
||||
});
|
||||
document.getElementById('inspectStage')?.addEventListener('mousemove', (e) => {
|
||||
const rect = e.currentTarget.getBoundingClientRect();
|
||||
inspectRotY = (e.clientX - rect.left - rect.width/2) / 5;
|
||||
inspectRotX = -(e.clientY - rect.top - rect.height/2) / 5;
|
||||
updateInspectTransform();
|
||||
});
|
||||
document.getElementById('inspectStage')?.addEventListener('wheel', (e) => {
|
||||
e.preventDefault(); inspectZoom += e.deltaY < 0 ? 0.1 : -0.1;
|
||||
inspectZoom = Math.max(0.5, Math.min(inspectZoom, 4)); updateInspectTransform();
|
||||
});
|
||||
function updateInspectTransform() { const obj = document.getElementById('inspectObject'); if(obj) obj.style.transform = `scale(${inspectZoom}) rotateX(${inspectRotX}deg) rotateY(${inspectRotY}deg)`; }
|
||||
document.getElementById('inspectClose')?.addEventListener('click', () => { document.getElementById('inspectModal').classList.remove('active'); });
|
||||
|
||||
/* --- Interaction --- */
|
||||
document.getElementById("btnZoomIn")?.addEventListener('click', () => { const r = workspace.getBoundingClientRect(); zoomWorkspace(1.2, r.width/2, r.height/2); });
|
||||
document.getElementById("btnZoomOut")?.addEventListener('click', () => { const r = workspace.getBoundingClientRect(); zoomWorkspace(1/1.2, r.width/2, r.height/2); });
|
||||
document.getElementById("btnZoomReset")?.addEventListener('click', () => { panX = 0; panY = 0; zoom = 1; updateViewport(); });
|
||||
|
||||
workspace.addEventListener('wheel', (e) => { e.preventDefault(); const wsRect = workspace.getBoundingClientRect(); zoomWorkspace(e.deltaY < 0 ? 1.1 : (1/1.1), e.clientX - wsRect.left, e.clientY - wsRect.top); });
|
||||
|
||||
workspace.addEventListener('mousedown', (e) => {
|
||||
const port = e.target.closest('.pb-port');
|
||||
if (port) {
|
||||
const nodeEl = port.closest('.pb-node');
|
||||
const portId = port.dataset.port;
|
||||
const existingIdx = connections.findIndex(c => (c.toNode === nodeEl.dataset.id && c.toPort === portId) || (c.fromNode === nodeEl.dataset.id && c.fromPort === portId));
|
||||
if (existingIdx !== -1) { connections.splice(existingIdx, 1); evaluateBuild(); renderWires(); return; }
|
||||
const coords = getPortCoords(nodeEl.dataset.id, portId);
|
||||
wiringStart = { node: nodeEl.dataset.id, port: portId, x: coords.x, y: coords.y };
|
||||
tempWirePath = { x: coords.x, y: coords.y }; return;
|
||||
}
|
||||
|
||||
const wire = e.target.closest('.pb-wire');
|
||||
if (wire && wire.dataset.connId) { clearSelection(); selectedWireId = wire.dataset.connId; renderWires(); e.stopPropagation(); return; }
|
||||
|
||||
const nodeEl = e.target.closest('.pb-node');
|
||||
if (nodeEl) {
|
||||
clearSelection(); selectedNodeId = nodeEl.dataset.id; nodeEl.classList.add('selected'); isDraggingNode = nodeEl.dataset.id;
|
||||
const rect = nodeEl.getBoundingClientRect(); dragOffset = { x: (e.clientX - rect.left) / zoom, y: (e.clientY - rect.top) / zoom };
|
||||
|
||||
// Unsnap from parent when picked up
|
||||
const node = nodes[isDraggingNode];
|
||||
if (node.snappedTo) {
|
||||
const parent = nodes[node.snappedTo.id];
|
||||
if (parent && parent.slots[node.snappedTo.key] === node.id) parent.slots[node.snappedTo.key] = null;
|
||||
node.snappedTo = null;
|
||||
node.el.style.zIndex = PC_PARTS[node.type].z; // Reset Z
|
||||
evaluateBuild();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clearSelection(); isPanning = true; panStart = { x: e.clientX - panX, y: e.clientY - panY };
|
||||
});
|
||||
|
||||
window.addEventListener('mousemove', (e) => {
|
||||
const wsRect = workspace.getBoundingClientRect();
|
||||
if (isPanning) { panX = e.clientX - panStart.x; panY = e.clientY - panStart.y; updateViewport(); return; }
|
||||
if (isDraggingNode) {
|
||||
const node = nodes[isDraggingNode];
|
||||
let newX = (e.clientX - wsRect.left - panX) / zoom - dragOffset.x;
|
||||
let newY = (e.clientY - wsRect.top - panY) / zoom - dragOffset.y;
|
||||
moveNodeRecursive(node.id, newX - node.x, newY - node.y);
|
||||
updateNodePositions();
|
||||
}
|
||||
if (wiringStart) { tempWirePath = { x: (e.clientX - wsRect.left - panX) / zoom, y: (e.clientY - wsRect.top - panY) / zoom }; renderWires(); }
|
||||
});
|
||||
|
||||
window.addEventListener('mouseup', (e) => {
|
||||
if (isDraggingNode) {
|
||||
const node = nodes[isDraggingNode];
|
||||
let snapped = false;
|
||||
|
||||
// Check all other nodes for compatible slots
|
||||
Object.values(nodes).forEach(target => {
|
||||
if (target.slots && !snapped && target.id !== node.id) {
|
||||
for(let slotKey in target.slots) {
|
||||
let slotDef = PC_PARTS[target.type].slots[slotKey];
|
||||
if(slotDef.accepts === node.type && target.slots[slotKey] === null) {
|
||||
let tX = target.x + slotDef.x; let tY = target.y + slotDef.y;
|
||||
if (Math.hypot(node.x - tX, node.y - tY) < 80) {
|
||||
moveNodeRecursive(node.id, tX - node.x, tY - node.y);
|
||||
node.snappedTo = { id: target.id, key: slotKey };
|
||||
target.slots[slotKey] = node.id;
|
||||
node.el.style.zIndex = PC_PARTS[target.type].z + 5; // Layer above parent
|
||||
snapped = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
isDraggingNode = null; updateNodePositions(); evaluateBuild();
|
||||
}
|
||||
|
||||
if (wiringStart) {
|
||||
const port = e.target.closest('.pb-port');
|
||||
if (port) {
|
||||
const targetNodeId = port.closest('.pb-node').dataset.id;
|
||||
const targetPortId = port.dataset.port;
|
||||
if (targetNodeId !== wiringStart.node) { connections.push({ id: `conn_${nextWireId++}`, fromNode: wiringStart.node, fromPort: wiringStart.port, toNode: targetNodeId, toPort: targetPortId }); }
|
||||
}
|
||||
wiringStart = null; tempWirePath = null; evaluateBuild(); renderWires();
|
||||
}
|
||||
isPanning = false;
|
||||
});
|
||||
|
||||
/* --- Deletion (Recursive) --- */
|
||||
function deleteNodeRecursive(id) {
|
||||
const n = nodes[id]; if(!n) return;
|
||||
if(n.slots) { Object.keys(n.slots).forEach(k => { if(typeof n.slots[k] === 'string') deleteNodeRecursive(n.slots[k]); }); }
|
||||
if(n.snappedTo) { const p = nodes[n.snappedTo.id]; if(p) p.slots[n.snappedTo.key] = null; }
|
||||
connections = connections.filter(c => c.fromNode !== id && c.toNode !== id);
|
||||
viewport.removeChild(n.el); delete nodes[id];
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', (e) => {
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedNodeId) {
|
||||
deleteNodeRecursive(selectedNodeId); clearSelection(); evaluateBuild(); renderWires();
|
||||
}
|
||||
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedWireId) {
|
||||
connections = connections.filter(c => c.id !== selectedWireId); clearSelection(); evaluateBuild(); renderWires();
|
||||
}
|
||||
});
|
||||
|
||||
workspace.addEventListener('dragover', (e) => { e.preventDefault(); });
|
||||
workspace.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
const type = e.dataTransfer.getData('spawnType');
|
||||
if (type) {
|
||||
const r = workspace.getBoundingClientRect();
|
||||
spawnNode(type, (e.clientX - r.left - panX) / zoom - (PC_PARTS[type].w / 2), (e.clientY - r.top - panY) / zoom - (PC_PARTS[type].h / 2));
|
||||
}
|
||||
});
|
||||
|
||||
btnClearBoard?.addEventListener('click', () => {
|
||||
viewport.querySelectorAll('.pb-node').forEach(el => el.remove());
|
||||
nodes = {}; connections = []; evaluateBuild(); renderWires();
|
||||
});
|
||||
|
||||
toolboxToggle?.addEventListener("click", () => {
|
||||
const c = pcPage?.classList.contains("toolboxCollapsed");
|
||||
pcPage.classList.toggle("toolboxCollapsed", !c);
|
||||
toolboxToggle?.setAttribute("aria-expanded", c ? "true" : "false");
|
||||
});
|
||||
|
||||
initToolbox(); evaluateBuild();
|
||||
})();
|
||||
@@ -1,52 +0,0 @@
|
||||
/* 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); }
|
||||
@@ -1,11 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "DSEG7";
|
||||
src: url("/fonts/DSEG7Classic-Regular.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.dseg {
|
||||
font-family: "DSEG7", monospace;
|
||||
letter-spacing: 0.15em;
|
||||
}
|
||||
@@ -23,10 +23,10 @@
|
||||
--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;
|
||||
--num-font: "DSEG7Classic"
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
@@ -34,8 +34,8 @@ html, body { height: 100%; }
|
||||
body { margin: 0; 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; }
|
||||
.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); margin-bottom: 25px; }
|
||||
.navInner { height: 90px; max-width: 1400px; margin: 0 auto; padding: 0 20px; display: flex; align-items: center; justify-content: space-between; gap: 24px; }
|
||||
.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--text); }
|
||||
.brandLogo { width: 2.5em; height: 2.5em; image-rendering: pixelated; }
|
||||
.brandName { letter-spacing: .12em; font-weight: 900; font-size: 18px; }
|
||||
@@ -188,3 +188,41 @@ body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--
|
||||
|
||||
@media (max-width: 1100px) { .binaryPage { --toolbox-w: 330px; } .denaryValue { font-size: 48px; } .hexValue { font-size: 40px; } .binaryValue { font-size: 32px; } }
|
||||
@media (max-width: 900px) { .binaryPage { --toolbox-w: 320px; } .bitsGrid { grid-template-columns: repeat(var(--cols), minmax(84px, 1fr)); } .hexGrid { grid-template-columns: repeat(var(--cols), minmax(130px, 1fr)); } }
|
||||
|
||||
.cc-sa:before {
|
||||
background-image: url(https://creativecommons.org/wp-content/themes/vocabulary-theme/vocabulary/svg/cc/icons/cc-icons.svg#cc-sa);
|
||||
float: left;
|
||||
margin-left: -2.5em;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.cc-nc:before {
|
||||
background-image: url(https://creativecommons.org/wp-content/themes/vocabulary-theme/vocabulary/svg/cc/icons/cc-icons.svg#cc-nc);
|
||||
float: left;
|
||||
margin-left: -2.5em;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.cc-by:before {
|
||||
background-image: url(https://creativecommons.org/wp-content/themes/vocabulary-theme/vocabulary/svg/cc/icons/cc-icons.svg#cc-by);
|
||||
float: left;
|
||||
margin-left: -2.5em;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
.cc-terms ul > li {
|
||||
padding-left: 2.5em;
|
||||
clear: both;
|
||||
list-style: none;
|
||||
margin-bottom: 2em;
|
||||
min-height: 2em;
|
||||
}
|
||||
|
||||
.cc-terms ul {
|
||||
padding: 0;
|
||||
font-size: 1.5rem;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 150%;
|
||||
margin: 0 0 2em 2em;
|
||||
}
|
||||
@@ -1,163 +1,165 @@
|
||||
/* === LOGIC GATES CANVAS CSS === */
|
||||
.lg-workspace {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-height: 750px;
|
||||
background-color: transparent;
|
||||
background-image: radial-gradient(rgba(255,255,255,0.12) 1px, transparent 1px);
|
||||
background-size: 24px 24px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
overflow: hidden;
|
||||
/* === FULL PAGE OVERRIDES FOR LOGIC GATES === */
|
||||
body:has(#logicPage) { overflow: hidden; }
|
||||
body:has(#logicPage) .pageWrap {
|
||||
max-width: 100% !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
height: calc(100vh - var(--nav-h));
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#logicPage { padding: 0 !important; margin: 0 !important; }
|
||||
|
||||
/* === MAIN CONTAINER === */
|
||||
.lg-container {
|
||||
flex: 1; display: flex; flex-direction: column; position: relative;
|
||||
width: 100%; height: 100%; overflow: hidden;
|
||||
}
|
||||
|
||||
.lg-svg-layer {
|
||||
/* === FIXED HEADER === */
|
||||
.lg-top-header {
|
||||
width: 100%; text-align: center; padding: 8px 20px 8px;
|
||||
background: var(--bg); z-index: 10; flex-shrink: 0;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.lg-title {
|
||||
font-family: var(--bit-font); font-size: 32px; letter-spacing: 0.12em;
|
||||
text-transform: uppercase; color: var(--text); margin: 0 0 2px 0; line-height: 1;
|
||||
}
|
||||
.lg-subtitle {
|
||||
color: var(--muted); font-size: 14px; font-family: var(--ui-font);
|
||||
font-weight: 500; margin: 0; line-height: 1.2;
|
||||
}
|
||||
.lg-subtitle kbd {
|
||||
background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 4px;
|
||||
font-family: var(--ui-font); color: #e8e8ee;
|
||||
}
|
||||
|
||||
/* === DYNAMIC CANVAS & CAMERA VIEWPORT === */
|
||||
.lg-workspace {
|
||||
flex: 1; position: relative; width: 100%;
|
||||
background-color: transparent;
|
||||
background-image: radial-gradient(rgba(255,255,255,0.15) 1px, transparent 1px);
|
||||
background-size: 24px 24px; overflow: hidden;
|
||||
cursor: grab; /* Indicates pannable area */
|
||||
}
|
||||
.lg-workspace:active { cursor: grabbing; }
|
||||
|
||||
.lg-viewport {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
transform-origin: 0 0;
|
||||
pointer-events: none; /* Let events reach workspace, nodes will re-enable it */
|
||||
}
|
||||
|
||||
/* Zoom UI Controls */
|
||||
.lg-zoom-controls {
|
||||
position: absolute; bottom: 20px; left: 20px; z-index: 100;
|
||||
display: flex; gap: 8px;
|
||||
}
|
||||
.lg-zoom-btn {
|
||||
width: 40px; height: 40px; border-radius: 8px; font-family: var(--ui-font);
|
||||
font-size: 22px; font-weight: 800; display: flex; align-items: center; justify-content: center;
|
||||
background: rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.15);
|
||||
color: #e8e8ee; cursor: pointer; backdrop-filter: blur(8px); transition: all 0.2s;
|
||||
}
|
||||
.lg-zoom-btn:hover { background: rgba(255,255,255,0.1); border-color: #28f07a; color: #28f07a; }
|
||||
|
||||
.lg-svg-layer {
|
||||
position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1;
|
||||
}
|
||||
|
||||
/* Wires */
|
||||
.lg-wire {
|
||||
stroke: rgba(255,255,255,0.25);
|
||||
stroke-width: 6;
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
stroke: rgba(255,255,255,0.25); stroke-width: 6; fill: none; stroke-linecap: round;
|
||||
transition: stroke 0.1s ease, filter 0.1s ease, stroke-width 0.1s ease;
|
||||
pointer-events: stroke; /* Allows wires to be clicked */
|
||||
cursor: pointer;
|
||||
}
|
||||
.lg-wire:hover {
|
||||
stroke: rgba(255,255,255,0.6);
|
||||
stroke-width: 10;
|
||||
}
|
||||
.lg-wire.active {
|
||||
stroke: #28f07a;
|
||||
filter: drop-shadow(0 0 6px rgba(40,240,122,0.6));
|
||||
pointer-events: stroke; cursor: pointer;
|
||||
}
|
||||
.lg-wire:hover { stroke: rgba(255,255,255,0.6); stroke-width: 10; }
|
||||
.lg-wire.active { stroke: #28f07a; filter: drop-shadow(0 0 6px rgba(40,240,122,0.6)); }
|
||||
.lg-wire.active:hover { stroke: #5dff9e; }
|
||||
.lg-wire.selected {
|
||||
stroke: #ff5555 !important;
|
||||
stroke-width: 8 !important;
|
||||
stroke-dasharray: 8 8;
|
||||
filter: drop-shadow(0 0 8px rgba(255,85,85,0.8)) !important;
|
||||
animation: wireDash 1s linear infinite;
|
||||
stroke: #ff5555 !important; stroke-width: 8 !important; stroke-dasharray: 8 8;
|
||||
filter: drop-shadow(0 0 8px rgba(255,85,85,0.8)) !important; animation: wireDash 1s linear infinite;
|
||||
}
|
||||
@keyframes wireDash { to { stroke-dashoffset: -16; } }
|
||||
.lg-wire-temp { stroke: rgba(255,255,255,0.4); stroke-dasharray: 8 8; pointer-events: none; }
|
||||
|
||||
.lg-wire-temp {
|
||||
stroke: rgba(255,255,255,0.4);
|
||||
stroke-dasharray: 8 8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Nodes (Borderless & Transparent) */
|
||||
/* Nodes */
|
||||
.lg-node {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
cursor: grab;
|
||||
z-index: 10;
|
||||
box-shadow: none;
|
||||
backdrop-filter: none;
|
||||
user-select: none;
|
||||
transition: filter 0.2s;
|
||||
position: absolute; background: transparent; border: none; border-radius: 0; padding: 4px;
|
||||
display: flex; flex-direction: column; align-items: center; cursor: grab;
|
||||
z-index: 10; user-select: none; transition: filter 0.2s;
|
||||
pointer-events: auto; /* Re-enables interaction inside the viewport */
|
||||
}
|
||||
.lg-node:active { cursor: grabbing; z-index: 20; }
|
||||
.lg-node.selected {
|
||||
filter: drop-shadow(0 0 10px rgba(255,85,85,0.8));
|
||||
}
|
||||
.lg-node.selected { filter: drop-shadow(0 0 10px rgba(255,85,85,0.8)); }
|
||||
|
||||
/* Node Labels (Seven-Segment, +2 Sizes Bigger) */
|
||||
.lg-header {
|
||||
font-size: 24px;
|
||||
color: var(--muted);
|
||||
font-family: var(--bit-font);
|
||||
letter-spacing: 2px;
|
||||
pointer-events: none;
|
||||
margin-bottom: 6px;
|
||||
font-size: 24px; color: var(--muted); font-family: var(--bit-font);
|
||||
letter-spacing: 2px; pointer-events: none; margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* Container mapping SVGs to absolutely positioned connection dots */
|
||||
.lg-gate-container {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lg-gate-svg {
|
||||
width: 100px;
|
||||
height: 50px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.lg-line-svg {
|
||||
width: 30px;
|
||||
height: 50px;
|
||||
display: block;
|
||||
}
|
||||
.lg-gate-container { position: relative; display: inline-flex; align-items: center; }
|
||||
.lg-gate-svg { width: 100px; height: 50px; display: block; }
|
||||
.lg-line-svg { width: 30px; height: 50px; display: block; }
|
||||
|
||||
/* Connection Ports */
|
||||
.lg-port {
|
||||
width: 16px; height: 16px; background: #a9acb8; border-radius: 50%; cursor: crosshair;
|
||||
border: 3px solid var(--bg); box-shadow: 0 0 0 1px rgba(255,255,255,0.2); transition: all 0.2s;
|
||||
position: absolute; z-index: 5;
|
||||
transform: translate(-50%, -50%); /* Centers the dot exactly over the coordinate */
|
||||
position: absolute; z-index: 5; transform: translate(-50%, -50%);
|
||||
}
|
||||
.lg-port:hover { transform: translate(-50%, -50%) scale(1.3); background: #fff; }
|
||||
.lg-port.active { background: #28f07a; box-shadow: 0 0 12px rgba(40,240,122,0.8); border-color: #1f2027; }
|
||||
|
||||
/* Draggable Toolbox Visual Gates Grid */
|
||||
.tb-icon-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
/* === FLOATING TOOLBOX === */
|
||||
.toolboxToggle {
|
||||
position: absolute; top: 10px; right: 20px; z-index: 90;
|
||||
display: flex; align-items: center; gap: 10px; padding: 8px 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); }
|
||||
|
||||
.lg-toolbox {
|
||||
position: absolute; top: 60px; right: 20px; bottom: 20px; width: var(--toolbox-w, 360px);
|
||||
z-index: 80; display: flex; flex-direction: column; gap: 16px; transform: translateX(0);
|
||||
transition: transform 420ms cubic-bezier(.2,.9,.2,1), opacity 220ms ease;
|
||||
overflow-y: auto; pointer-events: auto; padding-right: 6px;
|
||||
}
|
||||
.lg-toolbox::-webkit-scrollbar { width: 6px; }
|
||||
.lg-toolbox::-webkit-scrollbar-track { background: transparent; }
|
||||
.lg-toolbox::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.05); border-radius: 10px; transition: background 0.3s; }
|
||||
.lg-toolbox:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); }
|
||||
.lg-container.toolboxCollapsed .lg-toolbox { transform: translateX(calc(100% + 40px)); opacity: 0; pointer-events: none; }
|
||||
|
||||
.tb-icon-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.tb-icon-box {
|
||||
background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.15);
|
||||
border-radius: 12px; width: 100%; padding: 12px 0;
|
||||
background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.15); border-radius: 12px; width: 100%; padding: 12px 0;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
|
||||
cursor: grab; transition: background 0.2s, border-color 0.2s;
|
||||
}
|
||||
.tb-icon-box:hover { background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.3); }
|
||||
.tb-icon-label { font-family: var(--ui-font); font-size: 11px; font-weight: 800; color: var(--text); letter-spacing: 1px; text-transform: uppercase; }
|
||||
|
||||
/* Toolbox Scroll Fix */
|
||||
.panelCol {
|
||||
max-height: calc(100vh - var(--nav-h) - 30px) !important;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 30px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Truth Table */
|
||||
.tt-summary {
|
||||
font-family: var(--ui-font); font-size: 14px; font-weight: 800;
|
||||
color: var(--accent, #28f07a); cursor: pointer; user-select: none;
|
||||
outline: none; margin-bottom: 10px; text-transform: uppercase;
|
||||
font-family: var(--ui-font); font-size: 14px; font-weight: 800; color: var(--accent, #28f07a);
|
||||
cursor: pointer; user-select: none; outline: none; margin-bottom: 10px; text-transform: uppercase;
|
||||
}
|
||||
.tt-table-wrap {
|
||||
width: 100%; max-height: 300px; overflow-y: auto; overflow-x: auto;
|
||||
width: 100%; max-height: 250px; overflow-y: auto; overflow-x: auto;
|
||||
border-radius: 8px; border: 1px solid rgba(255,255,255,0.1); background: rgba(0,0,0,0.2);
|
||||
}
|
||||
.tt-table {
|
||||
width: 100%; border-collapse: collapse; text-align: center;
|
||||
font-family: var(--num-font); font-size: 14px; color: #e8e8ee;
|
||||
}
|
||||
.tt-table th {
|
||||
position: sticky; top: 0; background: rgba(31,32,39,0.95);
|
||||
padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.15);
|
||||
color: var(--muted); font-family: var(--bit-font); font-weight: normal;
|
||||
}
|
||||
.tt-table-wrap::-webkit-scrollbar { width: 6px; height: 6px; }
|
||||
.tt-table-wrap::-webkit-scrollbar-track { background: transparent; }
|
||||
.tt-table-wrap::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 10px; }
|
||||
.tt-table { width: 100%; border-collapse: collapse; text-align: center; font-family: var(--num-font); font-size: 14px; color: #e8e8ee; }
|
||||
.tt-table th { position: sticky; top: 0; background: rgba(31,32,39,0.95); padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.15); color: var(--muted); font-family: var(--bit-font); font-weight: normal; }
|
||||
.tt-table td { padding: 8px 12px; border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||||
.tt-table .tt-on { color: #28f07a; text-shadow: 0 0 8px rgba(40,240,122,0.5); }
|
||||
@@ -1,43 +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);
|
||||
}
|
||||
}
|
||||
114
src/styles/number-simulators.css
Normal file
114
src/styles/number-simulators.css
Normal file
@@ -0,0 +1,114 @@
|
||||
/* --- APP LAYOUT --- */
|
||||
.binaryPage {
|
||||
--toolbox-w: 360px;
|
||||
--toolbox-gap: 22px;
|
||||
--toolbox-toggle-top: calc(var(--nav-h) + 16px);
|
||||
--toolbox-top: calc(var(--toolbox-toggle-top) + 60px);
|
||||
position: relative; padding-top: 16px; flex: 1; display: flex; flex-direction: column;
|
||||
}
|
||||
.binaryPage:not(.toolboxCollapsed) { padding-right: calc(var(--toolbox-w) + var(--toolbox-gap)); }
|
||||
.binaryPage.toolboxCollapsed { padding-right: 0; }
|
||||
.topGrid { display: flex; align-items: stretch; gap: 28px; flex: 1; }
|
||||
.leftCol { flex: 1 1 auto; min-width: 0; container-type: inline-size; display: flex; flex-direction: column; }
|
||||
|
||||
/* --- READOUT FORMATTING --- */
|
||||
.readoutContainer { display: flex; align-items: center; justify-content: center; gap: 64px; width: 100%; }
|
||||
.readout { display: flex; flex-direction: column; align-items: center; gap: 16px; padding-top: 4px; }
|
||||
.readoutBlock { display: flex; flex-direction: column; align-items: center; gap: 8px; }
|
||||
.label { font-family: var(--bit-font); letter-spacing: .14em; text-transform: uppercase; font-size: 18px; opacity: .75; margin: 0; }
|
||||
.num { font-family: var(--num-font); color: #28f07a; text-shadow: 0 0 18px rgba(40,240,122,.35); letter-spacing: 2px; }
|
||||
|
||||
.denaryValue, .hexValue, .binaryValue { display: flex; gap: 16px; justify-content: center; white-space: pre-wrap; text-align: center; margin: 0; line-height: 1; }
|
||||
.denaryValue { font-size: 56px; }
|
||||
.hexValue { font-size: 48px; }
|
||||
.binaryValue { font-size: 40px; }
|
||||
|
||||
/* --- GRIDS & BITS --- */
|
||||
.bitsWrap { width: 100%; }
|
||||
.bitsGrid { --cols: 8; display: grid; grid-template-columns: repeat(var(--cols), minmax(92px, 1fr)); gap: 26px 22px; align-items: start; justify-items: center; }
|
||||
.bitsGrid.bitsFew { justify-content: center; }
|
||||
.bit { width: 100%; max-width: 140px; display: flex; flex-direction: column; align-items: center; gap: 8px; container-type: inline-size; }
|
||||
.bitVal { font-family: var(--bit-font); font-size: min(32px, calc(140cqw / var(--len, 1))); letter-spacing: 2px; color: rgba(232,232,238,.85); white-space: nowrap; line-height: 1; }
|
||||
|
||||
/* --- HEXADECIMAL --- */
|
||||
.hexGrid { --cols: 4; display: grid; grid-template-columns: repeat(var(--cols), minmax(160px, 1fr)); gap: 32px 20px; align-items: start; justify-items: center; width: 100%; }
|
||||
.hexGrid.bitsFew { justify-content: center; }
|
||||
.hexCol { display: flex; flex-direction: column; align-items: center; width: 100%; }
|
||||
|
||||
/* --- HEX COLOURS SPECIFIC --- */
|
||||
.colorGroupWrap { display: flex; flex-wrap: nowrap; gap: 16px; justify-content: center; width: 100%; }
|
||||
.colorGroup { display: flex; gap: 12px; padding: 12px; background: rgba(255,255,255,.02); border-radius: 20px; border: 1px solid rgba(255,255,255,.05); flex: 0 1 auto; min-width: 0; }
|
||||
.colorPreviewSide { display: flex; gap: 24px; align-items: center; justify-content: center; }
|
||||
.colorBoxWrap { display: flex; flex-direction: column; align-items: center; gap: 8px; }
|
||||
.colorBox { width: 60px; height: 60px; border-radius: 12px; border: 2px solid rgba(255,255,255,.15); box-shadow: 0 4px 16px rgba(0,0,0,.4); background-color: #000000; transition: background-color 0.2s ease; }
|
||||
.colorBoxLabel { font-family: var(--ui-font); font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
|
||||
|
||||
.text-red { color: #ff5555 !important; text-shadow: 0 0 18px rgba(255,85,85,.35) !important; }
|
||||
.text-green { color: #28f07a !important; text-shadow: 0 0 18px rgba(40,240,122,.35) !important; }
|
||||
.text-blue { color: #55aaff !important; text-shadow: 0 0 18px rgba(85,170,255,.35) !important; }
|
||||
|
||||
/* HEX CARD */
|
||||
.hexCard { background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.08); border-radius: 16px; padding: 16px 14px; display: flex; flex-direction: column; align-items: center; gap: 16px; width: 100%; max-width: 190px; flex: 0 1 auto; min-width: 0; box-shadow: 0 4px 24px rgba(0,0,0,.2); backdrop-filter: blur(10px); }
|
||||
.hexCardButtons { display: flex; gap: 10px; }
|
||||
.hexCardBtn { width: 38px; height: 38px; border-radius: 10px; border: 1px solid rgba(255,255,255,.12); font-family: var(--bit-font); font-size: 16px; font-weight: 900; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; color: rgba(232,232,238,.92); transition: all 0.2s ease; }
|
||||
.hexCardBtn.inc { background: rgba(40,240,122,.15); border-color: rgba(40,240,122,.25); }
|
||||
.hexCardBtn.inc:hover { background: rgba(40,240,122,.25); border-color: rgba(40,240,122,.4); }
|
||||
.hexCardBtn.dec { background: rgba(255,80,80,.18); border-color: rgba(255,80,80,.25); }
|
||||
.hexCardBtn.dec:hover { background: rgba(255,80,80,.28); border-color: rgba(255,80,80,.4); }
|
||||
.hexDigitDisplay { font-family: var(--num-font); font-size: 48px; color: #28f07a; text-shadow: 0 0 18px rgba(40,240,122,.35); line-height: 1; }
|
||||
.hexNibbleRow { display: flex; gap: 10px; justify-content: center; width: 100%; }
|
||||
.hexNibbleBit { display: flex; flex-direction: column; align-items: center; gap: 6px; }
|
||||
.hexNibbleBulb { width: 32px !important; height: 32px !important; margin-bottom: 2px !important; }
|
||||
.hexNibbleLabel { font-family: var(--bit-font); font-size: 24px; color: rgba(232,232,238,.6); }
|
||||
.hexColWeight { font-family: var(--bit-font); font-size: 40px; color: rgba(232,232,238,.6); margin-top: 14px; }
|
||||
|
||||
/* --- TOOLBOX COMPONENTS FOR NUMBERS --- */
|
||||
.panelCol { position: fixed; top: var(--toolbox-top); right: 22px; width: var(--toolbox-w); z-index: 80; display: flex; flex-direction: column; gap: 16px; transform: translateX(0); opacity: 1; transition: transform 420ms cubic-bezier(.2,.9,.2,1), opacity 220ms ease; }
|
||||
.binaryPage.toolboxCollapsed .panelCol { transform: translateX(calc(var(--toolbox-w) + 32px)); opacity: 0; pointer-events: none; }
|
||||
|
||||
.toggleRow { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
||||
.toggleLabel { font-family: var(--bit-font); font-size: 16px; letter-spacing: .12em; text-transform: uppercase; white-space: nowrap; color: var(--muted); transition: color 0.2s, text-shadow 0.2s; }
|
||||
.toggleLabel.activeMode { color: #28f07a; text-shadow: 0 0 12px rgba(40,240,122,.45); }
|
||||
.subCard { margin-top: 12px; padding: 12px; border-radius: 14px; border: 1px solid rgba(255,255,255,.10); background: rgba(0,0,0,.12); }
|
||||
.subTitle { font-family: var(--bit-font); font-weight: 900; letter-spacing: .14em; text-transform: uppercase; font-size: 16px; margin-bottom: 10px; opacity: .85; }
|
||||
.bitWidthRow { display: flex; align-items: center; gap: 10px; }
|
||||
.miniBtn { width: 44px; height: 44px; border-radius: 12px; border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.06); color: rgba(232,232,238,.9); font-family: var(--bit-font); font-weight: 900; font-size: 22px; cursor: pointer; }
|
||||
.miniBtn:hover { border-color: rgba(255,255,255,.22); }
|
||||
.bitInputWrap { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-radius: 12px; border: 1px solid rgba(255,255,255,.10); background: rgba(255,255,255,.04); padding: 10px 12px; }
|
||||
.bitInputLabel { font-family: var(--bit-font); font-size: 16px; letter-spacing: .12em; text-transform: uppercase; opacity: .7; }
|
||||
.bitInput { width: 70px; text-align: right; font-family: var(--num-font); font-size: 28px; letter-spacing: 2px; color: #28f07a; background: transparent; border: none; outline: none; }
|
||||
.controlsRow { display: flex; gap: 12px; margin-bottom: 12px; }
|
||||
.toolRowCentered { display: flex; justify-content: center; gap: 10px; margin-bottom: 10px; }
|
||||
.toolBtn { width: 46px; height: 46px; border-radius: 12px; border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.06); color: rgba(232,232,238,.92); font-family: var(--bit-font); font-size: 16px; font-weight: 900; cursor: pointer; }
|
||||
.toolDec { background: rgba(255,80,80,.20); border-color: rgba(255,80,80,.25); }
|
||||
.toolInc { background: rgba(40,240,122,.18); border-color: rgba(40,240,122,.25); }
|
||||
|
||||
/* === CONTAINER QUERIES === */
|
||||
@container (max-width: 1050px) {
|
||||
.readoutContainer { gap: 40px; }
|
||||
.colorGroupWrap { gap: 10px; }
|
||||
.colorGroup { padding: 10px; gap: 8px; border-radius: 16px; }
|
||||
.hexCard { padding: 12px 8px; width: 140px; gap: 12px; }
|
||||
.hexDigitDisplay { font-size: 40px; }
|
||||
.hexNibbleBulb { width: 24px !important; height: 24px !important; }
|
||||
.hexNibbleLabel { font-size: 20px; }
|
||||
.hexColWeight { font-size: 26px; margin-top: 10px; }
|
||||
.hexCardBtn { width: 34px; height: 34px; font-size: 14px; }
|
||||
}
|
||||
|
||||
@container (max-width: 800px) {
|
||||
.readoutContainer { flex-direction: column; gap: 24px; }
|
||||
.colorPreviewSide { padding-top: 0; }
|
||||
.colorGroupWrap { gap: 6px; }
|
||||
.colorGroup { padding: 6px; gap: 6px; border-radius: 12px; }
|
||||
.hexCard { padding: 8px 4px; width: 90px; gap: 8px; border-radius: 10px; }
|
||||
.hexDigitDisplay { font-size: 32px; }
|
||||
.hexNibbleBulb { width: 16px !important; height: 16px !important; }
|
||||
.hexNibbleLabel { font-size: 16px; }
|
||||
.hexColWeight { font-size: 20px; margin-top: 6px; }
|
||||
.hexCardBtn { width: 28px; height: 28px; font-size: 12px; }
|
||||
.denaryValue, .hexValue, .binaryValue { font-size: 32px; gap: 10px; }
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) { .binaryPage { --toolbox-w: 330px; } .denaryValue { font-size: 48px; } .hexValue { font-size: 40px; } .binaryValue { font-size: 32px; } }
|
||||
@media (max-width: 900px) { .binaryPage { --toolbox-w: 320px; } .bitsGrid { grid-template-columns: repeat(var(--cols), minmax(84px, 1fr)); } .hexGrid { grid-template-columns: repeat(var(--cols), minmax(130px, 1fr)); } }
|
||||
120
src/styles/pc-builder.css
Normal file
120
src/styles/pc-builder.css
Normal file
@@ -0,0 +1,120 @@
|
||||
/* === FULL PAGE OVERRIDES FOR PC BUILDER === */
|
||||
body:has(#pcPage) { overflow: hidden; }
|
||||
body:has(#pcPage) .pageWrap {
|
||||
max-width: 100% !important; padding: 0 !important; margin: 0 !important;
|
||||
height: calc(100vh - var(--nav-h)); display: flex; flex-direction: column;
|
||||
}
|
||||
#pcPage { padding: 0 !important; margin: 0 !important; }
|
||||
|
||||
/* === MAIN CONTAINER === */
|
||||
.pb-container { flex: 1; display: flex; flex-direction: column; position: relative; width: 100%; height: 100%; overflow: hidden; }
|
||||
|
||||
/* === FIXED HEADER === */
|
||||
.pb-top-header {
|
||||
width: 100%; text-align: center; padding: 8px 20px 8px;
|
||||
background: var(--bg); z-index: 10; flex-shrink: 0;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.pb-title { font-family: var(--bit-font); font-size: 32px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text); margin: 0 0 2px 0; line-height: 1; }
|
||||
.pb-subtitle { color: var(--muted); font-size: 14px; font-family: var(--ui-font); font-weight: 500; margin: 0; line-height: 1.2; }
|
||||
.pb-subtitle kbd { background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 4px; font-family: var(--ui-font); color: #e8e8ee; }
|
||||
|
||||
/* === DYNAMIC CANVAS === */
|
||||
.pb-workspace {
|
||||
flex: 1; position: relative; width: 100%; background-color: transparent;
|
||||
background-image: radial-gradient(rgba(255,255,255,0.08) 2px, transparent 2px);
|
||||
background-size: 32px 32px; overflow: hidden; cursor: grab;
|
||||
}
|
||||
.pb-workspace:active { cursor: grabbing; }
|
||||
.pb-viewport { position: absolute; inset: 0; width: 100%; height: 100%; transform-origin: 0 0; pointer-events: none; }
|
||||
|
||||
/* Zoom UI Controls */
|
||||
.pb-zoom-controls { position: absolute; bottom: 20px; left: 20px; z-index: 100; display: flex; gap: 8px; }
|
||||
.pb-zoom-btn {
|
||||
width: 40px; height: 40px; border-radius: 8px; font-family: var(--ui-font); font-size: 22px; font-weight: 800;
|
||||
display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,0.5);
|
||||
border: 1px solid rgba(255,255,255,0.15); color: #e8e8ee; cursor: pointer; backdrop-filter: blur(8px); transition: all 0.2s;
|
||||
}
|
||||
.pb-zoom-btn:hover { background: rgba(255,255,255,0.1); border-color: #55aaff; color: #55aaff; }
|
||||
|
||||
/* Wires sit at the VERY FRONT so they are never hidden in the case */
|
||||
.pb-svg-layer { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 100; pointer-events: none; }
|
||||
|
||||
/* Cables */
|
||||
.pb-wire {
|
||||
stroke: rgba(255,255,255,0.25); stroke-width: 6; fill: none; stroke-linecap: round;
|
||||
transition: stroke 0.1s ease, filter 0.1s ease, stroke-width 0.1s ease; pointer-events: stroke; cursor: pointer;
|
||||
}
|
||||
.pb-wire:hover { stroke: rgba(255,255,255,0.6); stroke-width: 10; }
|
||||
.pb-wire.active { stroke: #55aaff; filter: drop-shadow(0 0 6px rgba(85,170,255,0.6)); }
|
||||
.pb-wire.active:hover { stroke: #88ccff; }
|
||||
.pb-wire.selected { stroke: #ff5555 !important; stroke-width: 8 !important; stroke-dasharray: 8 8; filter: drop-shadow(0 0 8px rgba(255,85,85,0.8)) !important; animation: wireDash 1s linear infinite; }
|
||||
@keyframes wireDash { to { stroke-dashoffset: -16; } }
|
||||
.pb-wire-temp { stroke: rgba(255,255,255,0.4); stroke-dasharray: 8 8; pointer-events: none; }
|
||||
|
||||
/* PC Parts */
|
||||
.pb-node {
|
||||
position: absolute; background: transparent; border: none; border-radius: 0; padding: 0;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: grab;
|
||||
user-select: none; transition: filter 0.2s; pointer-events: auto;
|
||||
}
|
||||
.pb-node:active { cursor: grabbing; }
|
||||
.pb-node.selected { filter: drop-shadow(0 0 10px rgba(255,85,85,0.8)); }
|
||||
.pb-part-svg { width: 100%; height: 100%; display: block; pointer-events: none; filter: drop-shadow(0 10px 15px rgba(0,0,0,0.5)); }
|
||||
|
||||
/* Connection Ports */
|
||||
.pb-port {
|
||||
width: 14px; height: 14px; background: #222; border-radius: 50%; cursor: crosshair;
|
||||
border: 2px solid #55aaff; box-shadow: 0 0 0 1px rgba(255,255,255,0.2); transition: all 0.2s;
|
||||
position: absolute; z-index: 200; transform: translate(-50%, -50%); pointer-events: auto;
|
||||
}
|
||||
.pb-port:hover { transform: translate(-50%, -50%) scale(1.4); background: #fff; }
|
||||
.pb-port.active { background: #55aaff; box-shadow: 0 0 12px rgba(85,170,255,0.8); }
|
||||
|
||||
/* === ANIMATIONS (Triggered on Boot) === */
|
||||
@keyframes spin { 100% { transform: rotate(360deg); } }
|
||||
.system-running .fan-blades { animation: spin 0.4s linear infinite; }
|
||||
.system-running .monitor-screen { opacity: 1 !important; }
|
||||
|
||||
/* === FLOATING TOOLBOX === */
|
||||
.pb-toolbox {
|
||||
position: absolute; top: 60px; right: 20px; bottom: 20px; width: var(--toolbox-w, 360px);
|
||||
z-index: 80; display: flex; flex-direction: column; gap: 16px; transform: translateX(0);
|
||||
transition: transform 420ms cubic-bezier(.2,.9,.2,1), opacity 220ms ease; overflow-y: auto; pointer-events: auto; padding-right: 6px;
|
||||
}
|
||||
.pb-toolbox::-webkit-scrollbar { width: 6px; }
|
||||
.pb-toolbox::-webkit-scrollbar-track { background: transparent; }
|
||||
.pb-toolbox::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.05); border-radius: 10px; transition: background 0.3s; }
|
||||
.pb-toolbox:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); }
|
||||
.pb-container.toolboxCollapsed .pb-toolbox { transform: translateX(calc(100% + 40px)); opacity: 0; pointer-events: none; }
|
||||
|
||||
.tb-icon-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
|
||||
.tb-icon-box {
|
||||
background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; width: 100%; padding: 8px 0;
|
||||
display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; cursor: grab; transition: background 0.2s, border-color 0.2s;
|
||||
}
|
||||
.tb-icon-box:hover { background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.3); }
|
||||
.tb-icon-label { font-family: var(--ui-font); font-size: 10px; font-weight: 800; color: var(--text); letter-spacing: 0px; text-transform: uppercase; text-align: center;}
|
||||
|
||||
/* Diagnostics Panel */
|
||||
.specs-panel { width: 100%; border-radius: 8px; border: 1px solid rgba(255,255,255,0.1); background: rgba(0,0,0,0.4); padding: 12px; }
|
||||
.diag-cat { font-family: var(--ui-font); font-size: 12px; font-weight: 800; color: #55aaff; letter-spacing: 1px; text-transform: uppercase; margin: 12px 0 4px 0; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 2px;}
|
||||
.diag-cat:first-child { margin-top: 0; }
|
||||
.diag-row {
|
||||
display: flex; justify-content: space-between; font-family: var(--bit-font); font-size: 16px;
|
||||
letter-spacing: 1px; text-transform: uppercase; margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* === 3D INSPECT MODAL === */
|
||||
.inspect-modal {
|
||||
position: fixed; inset: 0; background: rgba(10,11,15,0.95); z-index: 1000;
|
||||
display: flex; align-items: center; justify-content: center; flex-direction: column;
|
||||
opacity: 0; pointer-events: none; transition: opacity 0.3s ease; backdrop-filter: blur(10px);
|
||||
}
|
||||
.inspect-modal.active { opacity: 1; pointer-events: auto; }
|
||||
.inspect-close { position: absolute; top: 30px; right: 40px; font-size: 40px; color: var(--muted); cursor: pointer; transition: color 0.2s; }
|
||||
.inspect-close:hover { color: #ff5555; }
|
||||
.inspect-stage { width: 600px; height: 600px; perspective: 1200px; display: flex; align-items: center; justify-content: center; margin-top: 20px;}
|
||||
.inspect-object { width: 100%; height: 100%; transform-style: preserve-3d; transition: transform 0.1s cubic-bezier(0.2, 0.8, 0.2, 1); display: flex; align-items: center; justify-content: center; }
|
||||
.inspect-object svg { width: 100%; height: 100%; filter: drop-shadow(0 30px 40px rgba(0,0,0,0.8)); }
|
||||
@@ -1,75 +0,0 @@
|
||||
:root{
|
||||
--bg: #1f2027;
|
||||
--panel: rgba(255,255,255,.04);
|
||||
--panel-border: rgba(255,255,255,.10);
|
||||
--text: #e8e8ee;
|
||||
--muted: #a9acb8;
|
||||
--accent: #33ff7a;
|
||||
--accent-dim: rgba(51,255,122,.15);
|
||||
--line: rgba(255,255,255,.12);
|
||||
}
|
||||
|
||||
*{ box-sizing: border-box; }
|
||||
|
||||
body{
|
||||
margin:0;
|
||||
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.site-header{
|
||||
border-bottom: 1px solid rgba(255,255,255,.08);
|
||||
background: rgba(0,0,0,.12);
|
||||
}
|
||||
|
||||
.site-header__inner{
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 14px 20px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
justify-content:space-between;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.brand{
|
||||
font-weight: 800;
|
||||
letter-spacing: .02em;
|
||||
}
|
||||
|
||||
.nav{
|
||||
display:flex;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
justify-content:flex-end;
|
||||
}
|
||||
|
||||
.nav__link{
|
||||
color: var(--muted);
|
||||
text-decoration:none;
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
.nav__link:hover{ color: var(--text); }
|
||||
|
||||
.site-main{
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 28px 20px 40px;
|
||||
min-height: calc(100vh - 140px);
|
||||
}
|
||||
|
||||
.site-footer{
|
||||
border-top: 1px solid rgba(255,255,255,.08);
|
||||
background: rgba(0,0,0,.10);
|
||||
}
|
||||
|
||||
.site-footer__inner{
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 20px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
Reference in New Issue
Block a user