You've already forked computing-box
Full initial 2.0 build
All checks were successful
Pre-release on non-main branches / prerelease (push) Successful in 31s
All checks were successful
Pre-release on non-main branches / prerelease (push) Successful in 31s
Signed-off-by: Alexander Lyall <alex@adcm.uk>
This commit is contained in:
21
dist/binary/index.html
vendored
21
dist/binary/index.html
vendored
@@ -1,3 +1,20 @@
|
|||||||
<!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.CswAWODG.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.
|
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>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Alexander Lyall</div> <div style="margin-top: 5px;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> •
|
||||||
|
<a href="/legal-code" style="color: var(--muted); text-decoration: underline;">Legal Code</a> </div> </div> </footer> </body></html>
|
||||||
21
dist/hexadecimal/index.html
vendored
21
dist/hexadecimal/index.html
vendored
@@ -1,3 +1,20 @@
|
|||||||
<!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.CswAWODG.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).
|
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>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Alexander Lyall</div> <div style="margin-top: 5px;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> •
|
||||||
|
<a href="/legal-code" style="color: var(--muted); text-decoration: underline;">Legal Code</a> </div> </div> </footer> </body></html>
|
||||||
26
dist/index.html
vendored
26
dist/index.html
vendored
@@ -1,7 +1,19 @@
|
|||||||
<!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%}
|
<!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>
|
||||||
</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>
|
var _paq = window._paq = window._paq || [];
|
||||||
To get started, open the <code data-astro-cid-mmc7otgs><pre data-astro-cid-mmc7otgs>src/pages</pre></code> directory in your project.
|
_paq.push(["setCookieDomain", "*.www.computingbox.co.uk"]);
|
||||||
</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>
|
_paq.push(["setDomains", ["*.www.computingbox.co.uk","*.csbox.mrdaviscsit.uk","*.csbox.mrlyall.co.uk","*.csbox.mrlyall.uk"]]);
|
||||||
From content layers to server islands, click to learn more about the new features and
|
_paq.push(["enableCrossDomainLinking"]);
|
||||||
improvements in Astro 5.0
|
_paq.push(["disableCookies"]);
|
||||||
</p> </a> </div> </body></html>
|
_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.CswAWODG.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>Computer Science Concept Simulators</div> <div>© 2026 Computing:Box • Created with ♥ by Alexander Lyall</div> <div style="margin-top: 5px;"> <a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> •
|
||||||
|
<a href="/legal-code" style="color: var(--muted); text-decoration: underline;">Legal Code</a> </div> </div> </footer> </body></html>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
import '../styles/global.css';
|
import "../styles/global.css";
|
||||||
|
|
||||||
const { title = "Computing:Box" } = Astro.props;
|
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" />
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
<title>{title}</title>
|
<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.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<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 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="/hexadecimal">Hexadecimal</a>
|
||||||
<a href="/hex-colours">Hex Colours</a>
|
<a href="/hex-colours">Hex Colours</a>
|
||||||
<a href="/logic-gates">Logic Gates</a>
|
<a href="/logic-gates">Logic Gates</a>
|
||||||
|
<a href="/pc-builder">PC Components</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -40,7 +56,11 @@ const { title = "Computing:Box" } = Astro.props;
|
|||||||
<footer class="siteFooter">
|
<footer class="siteFooter">
|
||||||
<div class="footerInner">
|
<div class="footerInner">
|
||||||
<div>Computer Science Concept Simulators</div>
|
<div>Computer Science Concept Simulators</div>
|
||||||
<div>© {new Date().getFullYear()} Computing:Box • Created with ♥ by Mr A Lyall</div>
|
<div>© {new Date().getFullYear()} Computing:Box • Created with ♥ by Alexander Lyall</div>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
<a href="/copyright" style="color: var(--muted); text-decoration: underline;">Copyright Notice</a> •
|
||||||
|
<a href="/legal-code" style="color: var(--muted); text-decoration: underline;">Legal Code</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
37
src/pages/about.astro
Normal file
37
src/pages/about.astro
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title="About | Computing:Box">
|
||||||
|
<article style="max-width: 900px; margin: 0 auto;">
|
||||||
|
<div class="card" style="margin-bottom: 40px;">
|
||||||
|
<h1 class="brandName" style="font-size: 32px; color: var(--text); margin-bottom: 16px;">The New Computing:Box Experience</h1>
|
||||||
|
<p style="color: var(--muted); font-size: 18px; line-height: 1.6;">
|
||||||
|
The platform has been rebuilt from the ground up to provide a deeper, more professional simulation environment.
|
||||||
|
We've introduced several key simulators and interface upgrades to support the modern Computing classroom:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
|
<ul style="list-style: none; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
||||||
|
<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 bit sizes)</li>
|
||||||
|
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Unified Binary Simulator (Unsigned & Two's Complement)</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 (GCSE & A Level)</li>
|
||||||
|
<li style="color: var(--text);"><span style="color: var(--accent);">✔</span> Enhanced Gate Simulator (Truth Table Creator)</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 class="card">
|
||||||
|
<h2 class="brandName" style="font-size: 24px; margin-bottom: 12px;">Educational Impact</h2>
|
||||||
|
<p style="color: var(--muted);">
|
||||||
|
Computing:Box is designed to help students learn computing concepts step by step, using clear visuals and simple interactions.
|
||||||
|
By changing values and seeing results straight away, students can build understanding at their own pace.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</BaseLayout>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import "../styles/number-simulators.css";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Binary Simulator | Computing:Box">
|
<BaseLayout title="Binary Simulator | Computing:Box">
|
||||||
|
|||||||
30
src/pages/copyright.astro
Normal file
30
src/pages/copyright.astro
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
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>
|
||||||
|
<p style="color: var(--text);">
|
||||||
|
<a href="https://www.computingbox.co.uk" style="color: var(--accent); text-decoration: none;">Computing:Box</a>
|
||||||
|
© 2024 by <a href="https://git.adcmnetworks.co.uk/alexander.lyall" style="color: var(--accent); text-decoration: none;">Alexander Lyall</a> is licensed under
|
||||||
|
<strong>Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International</strong>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<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><strong>Attribution</strong> — You must give appropriate credit, provide a link to the license, and indicate if changes were made.</li>
|
||||||
|
<li><strong>NonCommercial</strong> — You may not use the material for commercial purposes.</li>
|
||||||
|
<li><strong>ShareAlike</strong> — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import "../styles/number-simulators.css";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Hex Colours Simulator | Computing:Box">
|
<BaseLayout title="Hex Colours Simulator | Computing:Box">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
import "../styles/number-simulators.css";
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout title="Hexadecimal Simulator | Computing:Box">
|
<BaseLayout title="Hexadecimal Simulator | Computing:Box">
|
||||||
|
|||||||
23
src/pages/index.astro
Normal file
23
src/pages/index.astro
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<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>
|
||||||
65
src/pages/pc-builder.astro
Normal file
65
src/pages/pc-builder.astro
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
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 pb-wire-internal" id="wireLayerInternal"></svg>
|
||||||
|
|
||||||
|
<svg class="pb-svg-layer pb-wire-external" id="wireLayerExternal"></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>
|
||||||
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();
|
||||||
|
})();
|
||||||
@@ -47,39 +47,9 @@ body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--
|
|||||||
.siteFooter { border-top: 1px solid var(--line); background: rgba(0,0,0,.08); }
|
.siteFooter { border-top: 1px solid var(--line); background: rgba(0,0,0,.08); }
|
||||||
.footerInner { max-width: 1400px; margin: 0 auto; padding: 18px 20px; color: var(--muted); font-size: 12px; letter-spacing: .08em; display: flex; flex-direction: column; gap: 6px; }
|
.footerInner { max-width: 1400px; margin: 0 auto; padding: 18px 20px; color: var(--muted); font-size: 12px; letter-spacing: .08em; display: flex; flex-direction: column; gap: 6px; }
|
||||||
|
|
||||||
/* --- APP LAYOUT --- */
|
/* --- SHARED UI COMPONENTS (Used by ALL Simulators) --- */
|
||||||
.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; }
|
|
||||||
.divider { height: 1px; background: rgba(255,255,255,.08); margin: 16px 0 16px; }
|
.divider { height: 1px; background: rgba(255,255,255,.08); margin: 16px 0 16px; }
|
||||||
|
|
||||||
/* --- 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; }
|
|
||||||
|
|
||||||
.bulb { width: 44px; height: 44px; color: rgba(255,255,255,.15); margin-bottom: 8px; flex-shrink: 0; transition: 0.2s ease; background: transparent; display: flex; align-items: center; justify-content: center; }
|
.bulb { width: 44px; height: 44px; color: rgba(255,255,255,.15); margin-bottom: 8px; flex-shrink: 0; transition: 0.2s ease; background: transparent; display: flex; align-items: center; justify-content: center; }
|
||||||
.bulb svg { width: 100%; height: 100%; display: block; }
|
.bulb svg { width: 100%; height: 100%; display: block; }
|
||||||
.bulb.on { color: #ffd86b !important; filter: drop-shadow(0 0 14px rgba(255, 216, 107, 1)) !important; }
|
.bulb.on { color: #ffd86b !important; filter: drop-shadow(0 0 14px rgba(255, 216, 107, 1)) !important; }
|
||||||
@@ -92,99 +62,19 @@ body { margin: 0; background: var(--bg); color: var(--text); font-family: var(--
|
|||||||
.switch input:checked + .slider { background: rgba(40,240,122,.25); border-color: rgba(40,240,122,.30); }
|
.switch input:checked + .slider { background: rgba(40,240,122,.25); border-color: rgba(40,240,122,.30); }
|
||||||
.switch input:checked + .slider::before { transform: translateX(28px); }
|
.switch input:checked + .slider::before { transform: translateX(28px); }
|
||||||
|
|
||||||
/* --- 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 --- */
|
|
||||||
.toolboxToggle { position: fixed; top: var(--toolbox-toggle-top); right: 22px; z-index: 90; display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 12px; border: 1px solid rgba(255,255,255,.12); background: rgba(0,0,0,.15); backdrop-filter: blur(8px); color: rgba(232,232,238,.95); font-family: var(--bit-font); font-weight: 800; font-size: 16px; letter-spacing: .12em; text-transform: uppercase; cursor: pointer; }
|
|
||||||
.toolboxIcon { font-size: 20px; filter: drop-shadow(0 0 8px rgba(255,105,180,.35)); }
|
|
||||||
.toolboxToggle:hover { border-color: rgba(255,255,255,.22); }
|
|
||||||
.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; }
|
|
||||||
.card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.10); border-radius: 16px; padding: 16px; backdrop-filter: blur(10px); }
|
.card { background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.10); border-radius: 16px; padding: 16px; backdrop-filter: blur(10px); }
|
||||||
.cardTitle { font-family: var(--bit-font); font-weight: 900; letter-spacing: .14em; text-transform: uppercase; font-size: 18px; color: rgba(232,232,238,.9); margin-bottom: 12px; }
|
.cardTitle { font-family: var(--bit-font); font-weight: 900; letter-spacing: .14em; text-transform: uppercase; font-size: 18px; color: rgba(232,232,238,.9); margin-bottom: 12px; }
|
||||||
.hint { font-family: var(--bit-font); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: rgba(232,232,238,.55); margin-top: 10px; line-height: 1.35; }
|
.hint { font-family: var(--bit-font); font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: rgba(232,232,238,.55); margin-top: 10px; line-height: 1.35; }
|
||||||
.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; }
|
|
||||||
.btn { border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.06); color: rgba(232,232,238,.92); border-radius: 12px; padding: 10px 12px; font-family: var(--bit-font); font-size: 14px; letter-spacing: .12em; text-transform: uppercase; font-weight: 900; cursor: pointer; }
|
.btn { border: 1px solid rgba(255,255,255,.12); background: rgba(255,255,255,.06); color: rgba(232,232,238,.92); border-radius: 12px; padding: 10px 12px; font-family: var(--bit-font); font-size: 14px; letter-spacing: .12em; text-transform: uppercase; font-weight: 900; cursor: pointer; }
|
||||||
.btn:hover { border-color: rgba(255,255,255,.22); }
|
.btn:hover { border-color: rgba(255,255,255,.22); }
|
||||||
.btnAccent { background: rgba(40,240,122,.12); border-color: rgba(40,240,122,.22); }
|
.btnAccent { background: rgba(40,240,122,.12); border-color: rgba(40,240,122,.22); }
|
||||||
.btnAccent:hover { border-color: rgba(40,240,122,.35); }
|
.btnAccent:hover { border-color: rgba(40,240,122,.35); }
|
||||||
.btnHalf { width: calc(50% - 6px); }
|
.btnHalf { width: calc(50% - 6px); }
|
||||||
.btnWide { width: 100%; }
|
.btnWide { width: 100%; }
|
||||||
.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); }
|
|
||||||
.btnReset { color: rgba(232,232,238,.95); }
|
.btnReset { color: rgba(232,232,238,.95); }
|
||||||
.btnReset:hover { background: rgba(255,80,80,.18); border-color: rgba(255,80,80,.35); }
|
.btnReset:hover { background: rgba(255,80,80,.18); border-color: rgba(255,80,80,.35); }
|
||||||
|
|
||||||
/* === CONTAINER QUERIES === */
|
.toolboxToggle { position: fixed; top: var(--toolbox-toggle-top, calc(var(--nav-h) + 16px)); right: 22px; z-index: 90; display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 12px; border: 1px solid rgba(255,255,255,.12); background: rgba(0,0,0,.15); backdrop-filter: blur(8px); color: rgba(232,232,238,.95); font-family: var(--bit-font); font-weight: 800; font-size: 16px; letter-spacing: .12em; text-transform: uppercase; cursor: pointer; }
|
||||||
@container (max-width: 1050px) {
|
.toolboxIcon { font-size: 20px; filter: drop-shadow(0 0 8px rgba(255,105,180,.35)); }
|
||||||
.readoutContainer { gap: 40px; }
|
.toolboxToggle:hover { border-color: rgba(255,255,255,.22); }
|
||||||
.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)); } }
|
|
||||||
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)); }
|
||||||
Reference in New Issue
Block a user