From 85ca389f29f258adc3f846a90f9535fa54673703 Mon Sep 17 00:00:00 2001 From: DeNNiiInc Date: Sat, 27 Dec 2025 19:00:15 +1100 Subject: [PATCH] Add git commit info to UI and update deployment script --- .gitignore | 1 + deploy.sh | 4 + index.html | 470 ++++++++++++--------- script.js | 38 ++ styles.css | 1003 ++++++++++++++++++++++++--------------------- update-version.js | 28 ++ 6 files changed, 887 insertions(+), 657 deletions(-) create mode 100644 update-version.js diff --git a/.gitignore b/.gitignore index 69cb4b9..0f7d58a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ cron.log # System files .DS_Store Thumbs.db +version.json diff --git a/deploy.sh b/deploy.sh index 9336f07..08521e0 100644 --- a/deploy.sh +++ b/deploy.sh @@ -13,6 +13,10 @@ echo "Deploying $APP_NAME..." # If we are running this script FROM the repo, we assume we are already in the right place or we are setting it up. # This script assumes it is being run inside the destination directory /var/www/advanced-raid-calculator +# Generate version info +echo "Generating version info..." +node update-version.js + # Install dependencies echo "Installing dependencies..." npm install diff --git a/index.html b/index.html index 6ec5c7c..ab8906c 100644 --- a/index.html +++ b/index.html @@ -1,205 +1,295 @@ - - - - + + + Beyond Cloud Technology - RAID Calculator - - - - - - - + + + + + + + - +
-
-
-

- BCT Logo - Beyond Cloud Technology - RAID Calculator -

-

Professional storage capacity planning for RAID, Synology SHR, ZFS & Unraid - configurations +

+
+

+ BCT Logo + Beyond Cloud Technology - RAID Calculator +

+

+ Professional storage capacity planning for RAID, Synology SHR, ZFS & + Unraid configurations +

+ + + + + Watch on YouTube @beyondcloudtechnology + +
+
+ +
+
+ +
+

Configuration

+ +
+ + +
+ +
+ +
+ + 4 +
+
+ +
+ + +
+ + + + + +
+ +
+ + + Enable custom drive configuration +
+
+ + + + +
+ + +
+

Results

+ +
+
+ + + +

+ Configure your RAID array and click "Calculate Storage" to see + results

- - - - - Watch on YouTube @beyondcloudtechnology - +
-
+ +
-
-
- -
-

Configuration

- -
- - -
- -
- -
- - 4 -
-
- -
- - -
- - - - - -
- -
- - - Enable custom drive configuration -
-
- - - - -
- - -
-

Results

- -
-
- - - -

Configure your RAID array and click "Calculate Storage" to see results

-
-
-
+ +
+
+
+ + + +
+

Accurate Calculations

+

+ Accounts for binary vs decimal differences - 12TB drives show + actual ~10.9TB usable capacity +

+
- -
-
-
- - - - -
-

Accurate Calculations

-

Accounts for binary vs decimal differences - 12TB drives show actual ~10.9TB - usable capacity

-
- -
-
- - - -
-

Fault Tolerance

-

See exactly how many drives can fail while maintaining data integrity

-
- -
-
- - - -
-

Performance Insights

-

Understand read/write performance characteristics of each RAID configuration -

-
+
+
+ + +
-
+

Fault Tolerance

+

+ See exactly how many drives can fail while maintaining data + integrity +

+ - +
+
+ + + +
+

Performance Insights

+

+ Understand read/write performance characteristics of each RAID + configuration +

+
+ + + + +
- - - \ No newline at end of file + + diff --git a/script.js b/script.js index f69c9c0..871e65f 100644 --- a/script.js +++ b/script.js @@ -715,4 +715,42 @@ class RAIDCalculator { document.addEventListener('DOMContentLoaded', () => { new RAIDCalculator(); + initVersionDisplay(); }); + +/* =================================== + Version Display Logic + =================================== */ +function initVersionDisplay() { + const versionDisplay = document.getElementById('version-display'); + if (!versionDisplay) return; + + // Add cache buster to ensure fresh version info + fetch(`version.json?t=${new Date().getTime()}`) + .then(response => { + if (!response.ok) throw new Error('Version info not found'); + return response.json(); + }) + .then(data => { + if (!data.commit || !data.date) return; + + versionDisplay.innerHTML = ` + + + + + ${data.commit} + + ${data.date} + + `; + }) + .catch(error => { + console.log('Version info unavailable:', error); + versionDisplay.style.display = 'none'; + }); +} diff --git a/styles.css b/styles.css index bedbc49..24524b7 100644 --- a/styles.css +++ b/styles.css @@ -2,798 +2,867 @@ Design System & CSS Variables =================================== */ :root { - /* Color Palette - Modern Dark Theme */ - --color-bg-primary: #0a0e1a; - --color-bg-secondary: #111827; - --color-bg-tertiary: #1a2332; - --color-bg-card: rgba(30, 41, 59, 0.6); - --color-bg-card-hover: rgba(30, 41, 59, 0.8); + /* Color Palette - Modern Dark Theme */ + --color-bg-primary: #0a0e1a; + --color-bg-secondary: #111827; + --color-bg-tertiary: #1a2332; + --color-bg-card: rgba(30, 41, 59, 0.6); + --color-bg-card-hover: rgba(30, 41, 59, 0.8); - /* Accent Colors */ - --color-accent-primary: #3b82f6; - --color-accent-secondary: #8b5cf6; - --color-accent-success: #10b981; - --color-accent-warning: #f59e0b; - --color-accent-danger: #ef4444; + /* Accent Colors */ + --color-accent-primary: #3b82f6; + --color-accent-secondary: #8b5cf6; + --color-accent-success: #10b981; + --color-accent-warning: #f59e0b; + --color-accent-danger: #ef4444; - /* Gradients */ - --gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - --gradient-secondary: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); - --gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%); - --gradient-card: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%); + /* Gradients */ + --gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + --gradient-secondary: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); + --gradient-success: linear-gradient(135deg, #10b981 0%, #059669 100%); + --gradient-card: linear-gradient( + 135deg, + rgba(59, 130, 246, 0.1) 0%, + rgba(139, 92, 246, 0.1) 100% + ); - /* Text Colors */ - --color-text-primary: #f8fafc; - --color-text-secondary: #cbd5e1; - --color-text-tertiary: #94a3b8; - --color-text-muted: #64748b; + /* Text Colors */ + --color-text-primary: #f8fafc; + --color-text-secondary: #cbd5e1; + --color-text-tertiary: #94a3b8; + --color-text-muted: #64748b; - /* Border & Shadow */ - --color-border: rgba(148, 163, 184, 0.1); - --color-border-focus: rgba(59, 130, 246, 0.5); - --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); - --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2); - --shadow-glow: 0 0 20px rgba(59, 130, 246, 0.3); + /* Border & Shadow */ + --color-border: rgba(148, 163, 184, 0.1); + --color-border-focus: rgba(59, 130, 246, 0.5); + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3), + 0 4px 6px -2px rgba(0, 0, 0, 0.2); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), + 0 10px 10px -5px rgba(0, 0, 0, 0.2); + --shadow-glow: 0 0 20px rgba(59, 130, 246, 0.3); - /* Spacing */ - --spacing-xs: 0.5rem; - --spacing-sm: 0.75rem; - --spacing-md: 1rem; - --spacing-lg: 1.5rem; - --spacing-xl: 2rem; - --spacing-2xl: 3rem; + /* Spacing */ + --spacing-xs: 0.5rem; + --spacing-sm: 0.75rem; + --spacing-md: 1rem; + --spacing-lg: 1.5rem; + --spacing-xl: 2rem; + --spacing-2xl: 3rem; - /* Border Radius */ - --radius-sm: 0.375rem; - --radius-md: 0.5rem; - --radius-lg: 0.75rem; - --radius-xl: 1rem; + /* Border Radius */ + --radius-sm: 0.375rem; + --radius-md: 0.5rem; + --radius-lg: 0.75rem; + --radius-xl: 1rem; - /* Typography */ - --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - --font-size-xs: 0.75rem; - --font-size-sm: 0.875rem; - --font-size-base: 1rem; - --font-size-lg: 1.125rem; - --font-size-xl: 1.25rem; - --font-size-2xl: 1.5rem; - --font-size-3xl: 1.875rem; - --font-size-4xl: 2.25rem; + /* Typography */ + --font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", + sans-serif; + --font-size-xs: 0.75rem; + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.125rem; + --font-size-xl: 1.25rem; + --font-size-2xl: 1.5rem; + --font-size-3xl: 1.875rem; + --font-size-4xl: 2.25rem; - /* Transitions */ - --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1); + /* Transitions */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1); } /* =================================== Base Styles & Reset =================================== */ * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } body { - font-family: var(--font-family); - background: var(--color-bg-primary); - color: var(--color-text-primary); - line-height: 1.6; - min-height: 100vh; - overflow-x: hidden; + font-family: var(--font-family); + background: var(--color-bg-primary); + color: var(--color-text-primary); + line-height: 1.6; + min-height: 100vh; + overflow-x: hidden; } /* Animated Background */ body::before { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: - radial-gradient(circle at 20% 50%, rgba(59, 130, 246, 0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 50%), - radial-gradient(circle at 40% 20%, rgba(16, 185, 129, 0.05) 0%, transparent 50%); - z-index: -1; - animation: backgroundShift 20s ease-in-out infinite; + content: ""; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient( + circle at 20% 50%, + rgba(59, 130, 246, 0.1) 0%, + transparent 50% + ), + radial-gradient( + circle at 80% 80%, + rgba(139, 92, 246, 0.1) 0%, + transparent 50% + ), + radial-gradient( + circle at 40% 20%, + rgba(16, 185, 129, 0.05) 0%, + transparent 50% + ); + z-index: -1; + animation: backgroundShift 20s ease-in-out infinite; } @keyframes backgroundShift { + 0%, + 100% { + opacity: 1; + transform: scale(1); + } - 0%, - 100% { - opacity: 1; - transform: scale(1); - } - - 50% { - opacity: 0.8; - transform: scale(1.1); - } + 50% { + opacity: 0.8; + transform: scale(1.1); + } } /* =================================== Layout Components =================================== */ .container { - max-width: 1400px; - margin: 0 auto; - padding: var(--spacing-xl) var(--spacing-lg); + max-width: 1400px; + margin: 0 auto; + padding: var(--spacing-xl) var(--spacing-lg); } /* Header */ .header { - text-align: center; - margin-bottom: var(--spacing-lg); - animation: fadeInDown 0.6s ease-out; + text-align: center; + margin-bottom: var(--spacing-lg); + animation: fadeInDown 0.6s ease-out; } .header-content { - position: relative; + position: relative; } .title { - font-size: var(--font-size-3xl); - font-weight: 700; - background: var(--gradient-secondary); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - margin-bottom: var(--spacing-xs); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 0; - line-height: 1.2; + font-size: var(--font-size-3xl); + font-weight: 700; + background: var(--gradient-secondary); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: var(--spacing-xs); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0; + line-height: 1.2; } .title-icon { - width: 2.5rem; - height: 2.5rem; - filter: drop-shadow(0 0 10px rgba(59, 130, 246, 0.5)); - margin-bottom: var(--spacing-xs); + width: 2.5rem; + height: 2.5rem; + filter: drop-shadow(0 0 10px rgba(59, 130, 246, 0.5)); + margin-bottom: var(--spacing-xs); } .subtitle { - font-size: var(--font-size-base); - color: var(--color-text-secondary); - font-weight: 300; - margin-bottom: var(--spacing-sm); + font-size: var(--font-size-base); + color: var(--color-text-secondary); + font-weight: 300; + margin-bottom: var(--spacing-sm); } /* YouTube Link */ .youtube-link { - display: inline-flex; - align-items: center; - gap: var(--spacing-sm); - padding: var(--spacing-sm) var(--spacing-md); - background: linear-gradient(135deg, #FF0000 0%, #CC0000 100%); - color: white; - text-decoration: none; - border-radius: var(--radius-md); - font-weight: 600; - font-size: var(--font-size-sm); - box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3); - transition: all var(--transition-base); - animation: pulse-glow 2s ease-in-out infinite; - margin-top: var(--spacing-md); + display: inline-flex; + align-items: center; + gap: var(--spacing-sm); + padding: var(--spacing-sm) var(--spacing-md); + background: linear-gradient(135deg, #ff0000 0%, #cc0000 100%); + color: white; + text-decoration: none; + border-radius: var(--radius-md); + font-weight: 600; + font-size: var(--font-size-sm); + box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3); + transition: all var(--transition-base); + animation: pulse-glow 2s ease-in-out infinite; + margin-top: var(--spacing-md); } .youtube-link:hover { - transform: translateY(-2px) scale(1.03); - box-shadow: 0 4px 15px rgba(255, 0, 0, 0.5); - animation: none; + transform: translateY(-2px) scale(1.03); + box-shadow: 0 4px 15px rgba(255, 0, 0, 0.5); + animation: none; } .youtube-icon { - width: 1.25rem; - height: 1.25rem; - flex-shrink: 0; + width: 1.25rem; + height: 1.25rem; + flex-shrink: 0; } @keyframes pulse-glow { + 0%, + 100% { + box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3); + } - 0%, - 100% { - box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3); - } - - 50% { - box-shadow: 0 2px 15px rgba(255, 0, 0, 0.5); - } + 50% { + box-shadow: 0 2px 15px rgba(255, 0, 0, 0.5); + } } /* Main Content */ .main-content { - animation: fadeInUp 0.6s ease-out 0.2s both; + animation: fadeInUp 0.6s ease-out 0.2s both; } /* Card Component */ .card { - background: var(--color-bg-card); - backdrop-filter: blur(10px); - border: 1px solid var(--color-border); - border-radius: var(--radius-xl); - padding: var(--spacing-xl); - box-shadow: var(--shadow-lg); - transition: all var(--transition-base); + background: var(--color-bg-card); + backdrop-filter: blur(10px); + border: 1px solid var(--color-border); + border-radius: var(--radius-xl); + padding: var(--spacing-xl); + box-shadow: var(--shadow-lg); + transition: all var(--transition-base); } .card:hover { - background: var(--color-bg-card-hover); - box-shadow: var(--shadow-xl); - transform: translateY(-2px); + background: var(--color-bg-card-hover); + box-shadow: var(--shadow-xl); + transform: translateY(-2px); } /* Calculator Grid */ .calculator-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--spacing-xl); - margin-bottom: var(--spacing-2xl); + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--spacing-xl); + margin-bottom: var(--spacing-2xl); } @media (max-width: 968px) { - .calculator-grid { - grid-template-columns: 1fr; - } + .calculator-grid { + grid-template-columns: 1fr; + } } /* Section Title */ .section-title { - font-size: var(--font-size-2xl); - font-weight: 600; - margin-bottom: var(--spacing-lg); - color: var(--color-text-primary); - display: flex; - align-items: center; - gap: var(--spacing-sm); + font-size: var(--font-size-2xl); + font-weight: 600; + margin-bottom: var(--spacing-lg); + color: var(--color-text-primary); + display: flex; + align-items: center; + gap: var(--spacing-sm); } .section-title::before { - content: ''; - width: 4px; - height: 1.5rem; - background: var(--gradient-secondary); - border-radius: var(--radius-sm); + content: ""; + width: 4px; + height: 1.5rem; + background: var(--gradient-secondary); + border-radius: var(--radius-sm); } /* =================================== Form Components =================================== */ .form-group { - margin-bottom: var(--spacing-lg); + margin-bottom: var(--spacing-lg); } .label { - display: block; - font-size: var(--font-size-sm); - font-weight: 500; - color: var(--color-text-secondary); - margin-bottom: var(--spacing-sm); - text-transform: uppercase; - letter-spacing: 0.05em; + display: block; + font-size: var(--font-size-sm); + font-weight: 500; + color: var(--color-text-secondary); + margin-bottom: var(--spacing-sm); + text-transform: uppercase; + letter-spacing: 0.05em; } /* Select Input */ .select { - width: 100%; - padding: var(--spacing-md); - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - color: var(--color-text-primary); - font-size: var(--font-size-base); - font-family: var(--font-family); - cursor: pointer; - transition: all var(--transition-fast); + width: 100%; + padding: var(--spacing-md); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + color: var(--color-text-primary); + font-size: var(--font-size-base); + font-family: var(--font-family); + cursor: pointer; + transition: all var(--transition-fast); } .select:hover { - border-color: var(--color-accent-primary); + border-color: var(--color-accent-primary); } .select:focus { - outline: none; - border-color: var(--color-accent-primary); - box-shadow: 0 0 0 3px var(--color-border-focus); + outline: none; + border-color: var(--color-accent-primary); + box-shadow: 0 0 0 3px var(--color-border-focus); } .select option { - background: var(--color-bg-secondary); - color: var(--color-text-primary); + background: var(--color-bg-secondary); + color: var(--color-text-primary); } /* Slider Input */ .input-with-display { - display: flex; - align-items: center; - gap: var(--spacing-md); + display: flex; + align-items: center; + gap: var(--spacing-md); } .slider { - flex: 1; - -webkit-appearance: none; - appearance: none; - height: 8px; - background: var(--color-bg-secondary); - border-radius: var(--radius-lg); - outline: none; - transition: all var(--transition-fast); + flex: 1; + -webkit-appearance: none; + appearance: none; + height: 8px; + background: var(--color-bg-secondary); + border-radius: var(--radius-lg); + outline: none; + transition: all var(--transition-fast); } .slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 24px; - height: 24px; - background: var(--gradient-secondary); - border-radius: 50%; - cursor: pointer; - box-shadow: var(--shadow-md); - transition: all var(--transition-fast); + -webkit-appearance: none; + appearance: none; + width: 24px; + height: 24px; + background: var(--gradient-secondary); + border-radius: 50%; + cursor: pointer; + box-shadow: var(--shadow-md); + transition: all var(--transition-fast); } .slider::-webkit-slider-thumb:hover { - transform: scale(1.2); - box-shadow: var(--shadow-glow); + transform: scale(1.2); + box-shadow: var(--shadow-glow); } .slider::-moz-range-thumb { - width: 24px; - height: 24px; - background: var(--gradient-secondary); - border-radius: 50%; - cursor: pointer; - border: none; - box-shadow: var(--shadow-md); - transition: all var(--transition-fast); + width: 24px; + height: 24px; + background: var(--gradient-secondary); + border-radius: 50%; + cursor: pointer; + border: none; + box-shadow: var(--shadow-md); + transition: all var(--transition-fast); } .slider::-moz-range-thumb:hover { - transform: scale(1.2); - box-shadow: var(--shadow-glow); + transform: scale(1.2); + box-shadow: var(--shadow-glow); } .value-display { - min-width: 3rem; - text-align: center; - font-size: var(--font-size-lg); - font-weight: 600; - color: var(--color-accent-primary); + min-width: 3rem; + text-align: center; + font-size: var(--font-size-lg); + font-weight: 600; + color: var(--color-accent-primary); } /* Toggle Switch */ .toggle-container { - display: flex; - align-items: center; - gap: var(--spacing-md); + display: flex; + align-items: center; + gap: var(--spacing-md); } .toggle-input { - display: none; + display: none; } .toggle-label { - position: relative; - width: 50px; - height: 26px; - background: var(--color-bg-secondary); - border-radius: 13px; - cursor: pointer; - transition: all var(--transition-base); - border: 1px solid var(--color-border); + position: relative; + width: 50px; + height: 26px; + background: var(--color-bg-secondary); + border-radius: 13px; + cursor: pointer; + transition: all var(--transition-base); + border: 1px solid var(--color-border); } .toggle-slider { - position: absolute; - top: 2px; - left: 2px; - width: 20px; - height: 20px; - background: var(--color-text-tertiary); - border-radius: 50%; - transition: all var(--transition-base); + position: absolute; + top: 2px; + left: 2px; + width: 20px; + height: 20px; + background: var(--color-text-tertiary); + border-radius: 50%; + transition: all var(--transition-base); } -.toggle-input:checked+.toggle-label { - background: var(--gradient-secondary); - border-color: var(--color-accent-primary); +.toggle-input:checked + .toggle-label { + background: var(--gradient-secondary); + border-color: var(--color-accent-primary); } -.toggle-input:checked+.toggle-label .toggle-slider { - transform: translateX(24px); - background: white; +.toggle-input:checked + .toggle-label .toggle-slider { + transform: translateX(24px); + background: white; } .toggle-text { - font-size: var(--font-size-sm); - color: var(--color-text-secondary); + font-size: var(--font-size-sm); + color: var(--color-text-secondary); } /* Helper Text */ .helper-text { - font-size: var(--font-size-xs); - color: var(--color-text-muted); - margin-top: var(--spacing-xs); - font-style: italic; + font-size: var(--font-size-xs); + color: var(--color-text-muted); + margin-top: var(--spacing-xs); + font-style: italic; } /* Custom Drives Container */ .custom-drives-container { - margin-top: var(--spacing-md); - padding: var(--spacing-md); - background: var(--color-bg-secondary); - border-radius: var(--radius-md); - border: 1px solid var(--color-border); + margin-top: var(--spacing-md); + padding: var(--spacing-md); + background: var(--color-bg-secondary); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); } .drive-inputs { - display: grid; - gap: var(--spacing-sm); + display: grid; + gap: var(--spacing-sm); } .drive-input-row { - display: grid; - grid-template-columns: auto 1fr; - gap: var(--spacing-md); - align-items: center; + display: grid; + grid-template-columns: auto 1fr; + gap: var(--spacing-md); + align-items: center; } .drive-label { - font-size: var(--font-size-sm); - color: var(--color-text-tertiary); - min-width: 80px; + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + min-width: 80px; } .drive-select { - padding: var(--spacing-sm) var(--spacing-md); - background: var(--color-bg-tertiary); - border: 1px solid var(--color-border); - border-radius: var(--radius-sm); - color: var(--color-text-primary); - font-size: var(--font-size-sm); - font-family: var(--font-family); + padding: var(--spacing-sm) var(--spacing-md); + background: var(--color-bg-tertiary); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + color: var(--color-text-primary); + font-size: var(--font-size-sm); + font-family: var(--font-family); } /* Primary Button */ .btn-primary { - width: 100%; - padding: var(--spacing-md) var(--spacing-xl); - background: var(--gradient-secondary); - color: white; - border: none; - border-radius: var(--radius-md); - font-size: var(--font-size-lg); - font-weight: 600; - font-family: var(--font-family); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - gap: var(--spacing-sm); - box-shadow: var(--shadow-md); - transition: all var(--transition-base); - margin-top: var(--spacing-xl); + width: 100%; + padding: var(--spacing-md) var(--spacing-xl); + background: var(--gradient-secondary); + color: white; + border: none; + border-radius: var(--radius-md); + font-size: var(--font-size-lg); + font-weight: 600; + font-family: var(--font-family); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: var(--spacing-sm); + box-shadow: var(--shadow-md); + transition: all var(--transition-base); + margin-top: var(--spacing-xl); } .btn-primary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-glow), var(--shadow-lg); + transform: translateY(-2px); + box-shadow: var(--shadow-glow), var(--shadow-lg); } .btn-primary:active { - transform: translateY(0); + transform: translateY(0); } .btn-icon { - width: 1.5rem; - height: 1.5rem; + width: 1.5rem; + height: 1.5rem; } /* =================================== Results Panel =================================== */ .results-content { - min-height: 400px; + min-height: 400px; } .empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: 400px; - text-align: center; - color: var(--color-text-tertiary); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 400px; + text-align: center; + color: var(--color-text-tertiary); } .empty-icon { - width: 4rem; - height: 4rem; - margin-bottom: var(--spacing-lg); - opacity: 0.5; + width: 4rem; + height: 4rem; + margin-bottom: var(--spacing-lg); + opacity: 0.5; } /* Result Stats */ .result-stats { - display: grid; - gap: var(--spacing-lg); + display: grid; + gap: var(--spacing-lg); } .stat-card { - background: var(--color-bg-secondary); - padding: var(--spacing-lg); - border-radius: var(--radius-lg); - border: 1px solid var(--color-border); - transition: all var(--transition-base); + background: var(--color-bg-secondary); + padding: var(--spacing-lg); + border-radius: var(--radius-lg); + border: 1px solid var(--color-border); + transition: all var(--transition-base); } .stat-card:hover { - border-color: var(--color-accent-primary); - transform: translateX(4px); + border-color: var(--color-accent-primary); + transform: translateX(4px); } .stat-label { - font-size: var(--font-size-sm); - color: var(--color-text-tertiary); - text-transform: uppercase; - letter-spacing: 0.05em; - margin-bottom: var(--spacing-xs); + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: var(--spacing-xs); } .stat-value { - font-size: var(--font-size-3xl); - font-weight: 700; - color: var(--color-text-primary); - margin-bottom: var(--spacing-xs); + font-size: var(--font-size-3xl); + font-weight: 700; + color: var(--color-text-primary); + margin-bottom: var(--spacing-xs); } .stat-value.primary { - background: var(--gradient-secondary); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + background: var(--gradient-secondary); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; } .stat-subtext { - font-size: var(--font-size-sm); - color: var(--color-text-muted); + font-size: var(--font-size-sm); + color: var(--color-text-muted); } /* Capacity Bar */ .capacity-visualization { - margin: var(--spacing-xl) 0; + margin: var(--spacing-xl) 0; } .capacity-bar { - height: 40px; - background: var(--color-bg-secondary); - border-radius: var(--radius-lg); - overflow: hidden; - position: relative; - border: 1px solid var(--color-border); + height: 40px; + background: var(--color-bg-secondary); + border-radius: var(--radius-lg); + overflow: hidden; + position: relative; + border: 1px solid var(--color-border); } .capacity-fill { - height: 100%; - background: var(--gradient-success); - transition: width var(--transition-slow); - display: flex; - align-items: center; - justify-content: center; - font-weight: 600; - font-size: var(--font-size-sm); + height: 100%; + background: var(--gradient-success); + transition: width var(--transition-slow); + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: var(--font-size-sm); } .capacity-legend { - display: flex; - justify-content: space-between; - margin-top: var(--spacing-sm); - font-size: var(--font-size-sm); - color: var(--color-text-tertiary); + display: flex; + justify-content: space-between; + margin-top: var(--spacing-sm); + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); } /* Feature List */ .feature-list { - list-style: none; - margin-top: var(--spacing-lg); + list-style: none; + margin-top: var(--spacing-lg); } .feature-item { - display: flex; - align-items: flex-start; - gap: var(--spacing-md); - padding: var(--spacing-md); - margin-bottom: var(--spacing-sm); - background: var(--color-bg-secondary); - border-radius: var(--radius-md); - border: 1px solid var(--color-border); - transition: all var(--transition-fast); + display: flex; + align-items: flex-start; + gap: var(--spacing-md); + padding: var(--spacing-md); + margin-bottom: var(--spacing-sm); + background: var(--color-bg-secondary); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + transition: all var(--transition-fast); } .feature-item:hover { - border-color: var(--color-accent-primary); - background: var(--color-bg-tertiary); + border-color: var(--color-accent-primary); + background: var(--color-bg-tertiary); } .feature-icon { - width: 1.25rem; - height: 1.25rem; - flex-shrink: 0; - margin-top: 2px; + width: 1.25rem; + height: 1.25rem; + flex-shrink: 0; + margin-top: 2px; } .feature-icon.success { - stroke: var(--color-accent-success); + stroke: var(--color-accent-success); } .feature-icon.warning { - stroke: var(--color-accent-warning); + stroke: var(--color-accent-warning); } .feature-icon.info { - stroke: var(--color-accent-primary); + stroke: var(--color-accent-primary); } .feature-text { - flex: 1; + flex: 1; } .feature-title { - font-weight: 600; - color: var(--color-text-primary); - margin-bottom: var(--spacing-xs); + font-weight: 600; + color: var(--color-text-primary); + margin-bottom: var(--spacing-xs); } .feature-description { - font-size: var(--font-size-sm); - color: var(--color-text-tertiary); + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); } /* =================================== Info Grid =================================== */ .info-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: var(--spacing-lg); - margin-top: var(--spacing-2xl); + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: var(--spacing-lg); + margin-top: var(--spacing-2xl); } .info-card { - text-align: center; - padding: var(--spacing-xl); + text-align: center; + padding: var(--spacing-xl); } .info-icon { - width: 3rem; - height: 3rem; - margin: 0 auto var(--spacing-lg); - padding: var(--spacing-md); - border-radius: var(--radius-lg); - display: flex; - align-items: center; - justify-content: center; + width: 3rem; + height: 3rem; + margin: 0 auto var(--spacing-lg); + padding: var(--spacing-md); + border-radius: var(--radius-lg); + display: flex; + align-items: center; + justify-content: center; } .info-icon svg { - width: 100%; - height: 100%; + width: 100%; + height: 100%; } .info-icon-blue { - background: rgba(59, 130, 246, 0.1); - color: var(--color-accent-primary); + background: rgba(59, 130, 246, 0.1); + color: var(--color-accent-primary); } .info-icon-green { - background: rgba(16, 185, 129, 0.1); - color: var(--color-accent-success); + background: rgba(16, 185, 129, 0.1); + color: var(--color-accent-success); } .info-icon-purple { - background: rgba(139, 92, 246, 0.1); - color: var(--color-accent-secondary); + background: rgba(139, 92, 246, 0.1); + color: var(--color-accent-secondary); } .info-title { - font-size: var(--font-size-xl); - font-weight: 600; - margin-bottom: var(--spacing-sm); - color: var(--color-text-primary); + font-size: var(--font-size-xl); + font-weight: 600; + margin-bottom: var(--spacing-sm); + color: var(--color-text-primary); } .info-text { - font-size: var(--font-size-sm); - color: var(--color-text-tertiary); - line-height: 1.6; + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + line-height: 1.6; } /* =================================== Footer =================================== */ .footer { - text-align: center; - margin-top: var(--spacing-2xl); - padding: var(--spacing-xl) 0; - border-top: 1px solid var(--color-border); - color: var(--color-text-muted); - font-size: var(--font-size-sm); + text-align: center; + margin-top: var(--spacing-2xl); + padding: var(--spacing-xl) 0; + border-top: 1px solid var(--color-border); + color: var(--color-text-muted); + font-size: var(--font-size-sm); } /* =================================== Animations =================================== */ @keyframes fadeInDown { - from { - opacity: 0; - transform: translateY(-20px); - } + from { + opacity: 0; + transform: translateY(-20px); + } - to { - opacity: 1; - transform: translateY(0); - } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(20px); - } + from { + opacity: 0; + transform: translateY(20px); + } - to { - opacity: 1; - transform: translateY(0); - } + to { + opacity: 1; + transform: translateY(0); + } } @keyframes pulse { + 0%, + 100% { + opacity: 1; + } - 0%, - 100% { - opacity: 1; - } - - 50% { - opacity: 0.5; - } + 50% { + opacity: 0.5; + } } /* =================================== Responsive Design =================================== */ @media (max-width: 768px) { - .container { - padding: var(--spacing-md); - } + .container { + padding: var(--spacing-md); + } - .title { - font-size: var(--font-size-3xl); - } + .title { + font-size: var(--font-size-3xl); + } - .subtitle { - font-size: var(--font-size-base); - } + .subtitle { + font-size: var(--font-size-base); + } - .info-grid { - grid-template-columns: 1fr; - } + .info-grid { + grid-template-columns: 1fr; + } - .stat-value { - font-size: var(--font-size-2xl); - } + .stat-value { + font-size: var(--font-size-2xl); + } } /* =================================== Utility Classes =================================== */ .hidden { - display: none !important; -} \ No newline at end of file + display: none !important; +} + +/* =================================== + Version Tag + =================================== */ +.version-tag { + position: fixed; + bottom: 20px; + right: 20px; + display: flex; + align-items: center; + gap: 12px; + background: rgba(30, 41, 59, 0.85); + backdrop-filter: blur(8px); + padding: 8px 16px; + border-radius: 9999px; /* Pill shape */ + border: 1px solid rgba(255, 255, 255, 0.1); + font-size: var(--font-size-xs); + font-weight: 500; + color: var(--color-text-secondary); + z-index: 1000; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + transition: all var(--transition-base); + text-decoration: none; + font-family: var(--font-family); +} + +.version-tag:hover { + background: rgba(30, 41, 59, 0.95); + border-color: rgba(255, 255, 255, 0.2); + transform: translateY(-2px); + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +.version-commit { + font-family: "Consolas", "Monaco", monospace; + color: var(--color-accent-secondary); + font-weight: 600; + letter-spacing: 0.05em; +} + +.version-divider { + width: 4px; + height: 4px; + background-color: var(--color-text-tertiary); + border-radius: 50%; + opacity: 0.5; +} + +.version-date { + color: var(--color-text-muted); +} diff --git a/update-version.js b/update-version.js new file mode 100644 index 0000000..bc43aa9 --- /dev/null +++ b/update-version.js @@ -0,0 +1,28 @@ +const fs = require('fs'); +const { execSync } = require('child_process'); + +try { + console.log('Generating version information...'); + const commit = execSync('git rev-parse --short HEAD').toString().trim(); + const date = execSync('git log -1 --format=%cd --date=relative').toString().trim(); + + const versionInfo = { + commit: commit, + date: date + }; + + fs.writeFileSync('version.json', JSON.stringify(versionInfo, null, 2)); + console.log('version.json updated:', versionInfo); +} catch (error) { + console.error('Failed to update version info:', error); + // Fallback if git fails (e.g. not a repo) + const versionInfo = { + commit: 'unknown', + date: 'unknown' + }; + try { + fs.writeFileSync('version.json', JSON.stringify(versionInfo, null, 2)); + } catch (e) { + console.error('Failed to write fallback version.json', e); + } +}