Add git commit info to UI and update deployment script

This commit is contained in:
2025-12-27 19:00:15 +11:00
parent 2b690f2dac
commit 85ca389f29
6 changed files with 887 additions and 657 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ cron.log
# System files # System files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
version.json

View File

@@ -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. # 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 # 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 # Install dependencies
echo "Installing dependencies..." echo "Installing dependencies..."
npm install npm install

View File

@@ -1,35 +1,45 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="UTF-8" />
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Beyond Cloud Technology - RAID Calculator</title> <title>Beyond Cloud Technology - RAID Calculator</title>
<meta name="description" <meta
content="Professional RAID calculator by Beyond Cloud Technology supporting standard RAID, Synology SHR, ZFS, and Unraid configurations with accurate usable storage calculations."> name="description"
<link rel="icon" type="image/png" href="Logo.png"> content="Professional RAID calculator by Beyond Cloud Technology supporting standard RAID, Synology SHR, ZFS, and Unraid configurations with accurate usable storage calculations."
<link rel="stylesheet" href="styles.css"> />
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="icon" type="image/png" href="Logo.png" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="styles.css" />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> <link rel="preconnect" href="https://fonts.googleapis.com" />
</head> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body> <body>
<div class="container"> <div class="container">
<header class="header"> <header class="header">
<div class="header-content"> <div class="header-content">
<h1 class="title"> <h1 class="title">
<img src="Logo.png" alt="BCT Logo" class="title-icon"> <img src="Logo.png" alt="BCT Logo" class="title-icon" />
Beyond Cloud Technology - RAID Calculator Beyond Cloud Technology - RAID Calculator
</h1> </h1>
<p class="subtitle">Professional storage capacity planning for RAID, Synology SHR, ZFS & Unraid <p class="subtitle">
configurations Professional storage capacity planning for RAID, Synology SHR, ZFS &
Unraid configurations
</p> </p>
<a href="https://www.youtube.com/@beyondcloudtechnology" target="_blank" rel="noopener noreferrer" <a
class="youtube-link"> href="https://www.youtube.com/@beyondcloudtechnology"
target="_blank"
rel="noopener noreferrer"
class="youtube-link"
>
<svg class="youtube-icon" viewBox="0 0 24 24" fill="currentColor"> <svg class="youtube-icon" viewBox="0 0 24 24" fill="currentColor">
<path <path
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" /> d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
/>
</svg> </svg>
Watch on YouTube @beyondcloudtechnology Watch on YouTube @beyondcloudtechnology
</a> </a>
@@ -46,7 +56,9 @@
<label for="raid-type" class="label">RAID Type</label> <label for="raid-type" class="label">RAID Type</label>
<select id="raid-type" class="select"> <select id="raid-type" class="select">
<optgroup label="Standard RAID"> <optgroup label="Standard RAID">
<option value="raid0">RAID 0 - Striping (No Redundancy)</option> <option value="raid0">
RAID 0 - Striping (No Redundancy)
</option>
<option value="raid1">RAID 1 - Mirroring</option> <option value="raid1">RAID 1 - Mirroring</option>
<option value="raid5" selected>RAID 5 - Single Parity</option> <option value="raid5" selected>RAID 5 - Single Parity</option>
<option value="raid6">RAID 6 - Double Parity</option> <option value="raid6">RAID 6 - Double Parity</option>
@@ -54,7 +66,9 @@
</optgroup> </optgroup>
<optgroup label="Synology RAID"> <optgroup label="Synology RAID">
<option value="shr">SHR - Synology Hybrid RAID</option> <option value="shr">SHR - Synology Hybrid RAID</option>
<option value="shr2">SHR-2 - Synology Hybrid RAID (2-disk fault tolerance)</option> <option value="shr2">
SHR-2 - Synology Hybrid RAID (2-disk fault tolerance)
</option>
</optgroup> </optgroup>
<optgroup label="ZFS RAID"> <optgroup label="ZFS RAID">
<option value="zfs-stripe">ZFS Stripe (No Redundancy)</option> <option value="zfs-stripe">ZFS Stripe (No Redundancy)</option>
@@ -73,7 +87,14 @@
<div class="form-group"> <div class="form-group">
<label for="num-drives" class="label">Number of Drives</label> <label for="num-drives" class="label">Number of Drives</label>
<div class="input-with-display"> <div class="input-with-display">
<input type="range" id="num-drives" class="slider" min="2" max="24" value="4"> <input
type="range"
id="num-drives"
class="slider"
min="2"
max="24"
value="4"
/>
<span id="num-drives-display" class="value-display">4</span> <span id="num-drives-display" class="value-display">4</span>
</div> </div>
</div> </div>
@@ -98,20 +119,42 @@
</select> </select>
</div> </div>
<div id="zfs-vdev-config" class="form-group" style="display: none;"> <div id="zfs-vdev-config" class="form-group" style="display: none">
<label for="num-vdevs" class="label">Number of vdevs</label> <label for="num-vdevs" class="label">Number of vdevs</label>
<div class="input-with-display"> <div class="input-with-display">
<input type="range" id="num-vdevs" class="slider" min="1" max="8" value="2"> <input
type="range"
id="num-vdevs"
class="slider"
min="1"
max="8"
value="2"
/>
<span id="num-vdevs-display" class="value-display">2</span> <span id="num-vdevs-display" class="value-display">2</span>
</div> </div>
<p class="helper-text">Each vdev will use the selected RAID type (mirror/RAIDZ)</p> <p class="helper-text">
Each vdev will use the selected RAID type (mirror/RAIDZ)
</p>
</div> </div>
<div id="drives-per-vdev-config" class="form-group" style="display: none;"> <div
id="drives-per-vdev-config"
class="form-group"
style="display: none"
>
<label for="drives-per-vdev" class="label">Drives per vdev</label> <label for="drives-per-vdev" class="label">Drives per vdev</label>
<div class="input-with-display"> <div class="input-with-display">
<input type="range" id="drives-per-vdev" class="slider" min="2" max="12" value="3"> <input
<span id="drives-per-vdev-display" class="value-display">3</span> type="range"
id="drives-per-vdev"
class="slider"
min="2"
max="12"
value="3"
/>
<span id="drives-per-vdev-display" class="value-display"
>3</span
>
</div> </div>
<p class="helper-text" id="total-drives-text">Total drives: 6</p> <p class="helper-text" id="total-drives-text">Total drives: 6</p>
</div> </div>
@@ -119,20 +162,32 @@
<div class="form-group"> <div class="form-group">
<label class="label">Mixed Drive Sizes</label> <label class="label">Mixed Drive Sizes</label>
<div class="toggle-container"> <div class="toggle-container">
<input type="checkbox" id="mixed-drives" class="toggle-input"> <input type="checkbox" id="mixed-drives" class="toggle-input" />
<label for="mixed-drives" class="toggle-label"> <label for="mixed-drives" class="toggle-label">
<span class="toggle-slider"></span> <span class="toggle-slider"></span>
</label> </label>
<span class="toggle-text">Enable custom drive configuration</span> <span class="toggle-text"
>Enable custom drive configuration</span
>
</div> </div>
</div> </div>
<div id="custom-drives-container" class="custom-drives-container" style="display: none;"> <div
id="custom-drives-container"
class="custom-drives-container"
style="display: none"
>
<div id="drive-inputs" class="drive-inputs"></div> <div id="drive-inputs" class="drive-inputs"></div>
</div> </div>
<button id="calculate-btn" class="btn-primary"> <button id="calculate-btn" class="btn-primary">
<svg class="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
class="btn-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg> </svg>
Calculate Storage Calculate Storage
@@ -145,12 +200,21 @@
<div id="results-content" class="results-content"> <div id="results-content" class="results-content">
<div class="empty-state"> <div class="empty-state">
<svg class="empty-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" <svg
stroke-width="2"> class="empty-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path <path
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" /> d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
/>
</svg> </svg>
<p>Configure your RAID array and click "Calculate Storage" to see results</p> <p>
Configure your RAID array and click "Calculate Storage" to see
results
</p>
</div> </div>
</div> </div>
</section> </section>
@@ -160,46 +224,72 @@
<div class="info-grid"> <div class="info-grid">
<div class="info-card card"> <div class="info-card card">
<div class="info-icon info-icon-blue"> <div class="info-icon info-icon-blue">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="10" />
<path d="M12 16v-4m0-4h.01" /> <path d="M12 16v-4m0-4h.01" />
</svg> </svg>
</div> </div>
<h3 class="info-title">Accurate Calculations</h3> <h3 class="info-title">Accurate Calculations</h3>
<p class="info-text">Accounts for binary vs decimal differences - 12TB drives show actual ~10.9TB <p class="info-text">
usable capacity</p> Accounts for binary vs decimal differences - 12TB drives show
actual ~10.9TB usable capacity
</p>
</div> </div>
<div class="info-card card"> <div class="info-card card">
<div class="info-icon info-icon-green"> <div class="info-icon info-icon-green">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path <path
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /> d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
/>
</svg> </svg>
</div> </div>
<h3 class="info-title">Fault Tolerance</h3> <h3 class="info-title">Fault Tolerance</h3>
<p class="info-text">See exactly how many drives can fail while maintaining data integrity</p> <p class="info-text">
See exactly how many drives can fail while maintaining data
integrity
</p>
</div> </div>
<div class="info-card card"> <div class="info-card card">
<div class="info-icon info-icon-purple"> <div class="info-icon info-icon-purple">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="M13 10V3L4 14h7v7l9-11h-7z" /> <path d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg> </svg>
</div> </div>
<h3 class="info-title">Performance Insights</h3> <h3 class="info-title">Performance Insights</h3>
<p class="info-text">Understand read/write performance characteristics of each RAID configuration <p class="info-text">
Understand read/write performance characteristics of each RAID
configuration
</p> </p>
</div> </div>
</div> </div>
</main> </main>
<footer class="footer"> <footer class="footer">
<p>&copy; 2025 Beyond Cloud Technology. Professional storage planning tool.</p> <p>
&copy; 2025 Beyond Cloud Technology. Professional storage planning
tool.
</p>
</footer> </footer>
</div> </div>
<div id="version-display"></div>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

View File

@@ -715,4 +715,42 @@ class RAIDCalculator {
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new RAIDCalculator(); 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 = `
<a href="https://github.com/DeNNiiInc/Advanced-Raid-Calculator/commit/${data.commit}"
target="_blank"
rel="noopener noreferrer"
class="version-tag"
title="View commit on GitHub">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; opacity: 0.7;">
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
</svg>
<span class="version-commit">${data.commit}</span>
<span class="version-divider"></span>
<span class="version-date">${data.date}</span>
</a>
`;
})
.catch(error => {
console.log('Version info unavailable:', error);
versionDisplay.style.display = 'none';
});
}

View File

@@ -20,7 +20,11 @@
--gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%); --gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--gradient-secondary: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); --gradient-secondary: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%);
--gradient-success: linear-gradient(135deg, #10b981 0%, #059669 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%); --gradient-card: linear-gradient(
135deg,
rgba(59, 130, 246, 0.1) 0%,
rgba(139, 92, 246, 0.1) 100%
);
/* Text Colors */ /* Text Colors */
--color-text-primary: #f8fafc; --color-text-primary: #f8fafc;
@@ -32,9 +36,12 @@
--color-border: rgba(148, 163, 184, 0.1); --color-border: rgba(148, 163, 184, 0.1);
--color-border-focus: rgba(59, 130, 246, 0.5); --color-border-focus: rgba(59, 130, 246, 0.5);
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); --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-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2); --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); --shadow-glow: 0 0 20px rgba(59, 130, 246, 0.3);
/* Spacing */ /* Spacing */
@@ -52,7 +59,8 @@
--radius-xl: 1rem; --radius-xl: 1rem;
/* Typography */ /* Typography */
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; --font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
--font-size-xs: 0.75rem; --font-size-xs: 0.75rem;
--font-size-sm: 0.875rem; --font-size-sm: 0.875rem;
--font-size-base: 1rem; --font-size-base: 1rem;
@@ -88,22 +96,32 @@ body {
/* Animated Background */ /* Animated Background */
body::before { body::before {
content: ''; content: "";
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: background: radial-gradient(
radial-gradient(circle at 20% 50%, rgba(59, 130, 246, 0.1) 0%, transparent 50%), circle at 20% 50%,
radial-gradient(circle at 80% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 50%), rgba(59, 130, 246, 0.1) 0%,
radial-gradient(circle at 40% 20%, rgba(16, 185, 129, 0.05) 0%, transparent 50%); 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; z-index: -1;
animation: backgroundShift 20s ease-in-out infinite; animation: backgroundShift 20s ease-in-out infinite;
} }
@keyframes backgroundShift { @keyframes backgroundShift {
0%, 0%,
100% { 100% {
opacity: 1; opacity: 1;
@@ -172,7 +190,7 @@ body::before {
align-items: center; align-items: center;
gap: var(--spacing-sm); gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-md); padding: var(--spacing-sm) var(--spacing-md);
background: linear-gradient(135deg, #FF0000 0%, #CC0000 100%); background: linear-gradient(135deg, #ff0000 0%, #cc0000 100%);
color: white; color: white;
text-decoration: none; text-decoration: none;
border-radius: var(--radius-md); border-radius: var(--radius-md);
@@ -197,7 +215,6 @@ body::before {
} }
@keyframes pulse-glow { @keyframes pulse-glow {
0%, 0%,
100% { 100% {
box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3); box-shadow: 0 2px 8px rgba(255, 0, 0, 0.3);
@@ -256,7 +273,7 @@ body::before {
} }
.section-title::before { .section-title::before {
content: ''; content: "";
width: 4px; width: 4px;
height: 1.5rem; height: 1.5rem;
background: var(--gradient-secondary); background: var(--gradient-secondary);
@@ -401,12 +418,12 @@ body::before {
transition: all var(--transition-base); transition: all var(--transition-base);
} }
.toggle-input:checked+.toggle-label { .toggle-input:checked + .toggle-label {
background: var(--gradient-secondary); background: var(--gradient-secondary);
border-color: var(--color-accent-primary); border-color: var(--color-accent-primary);
} }
.toggle-input:checked+.toggle-label .toggle-slider { .toggle-input:checked + .toggle-label .toggle-slider {
transform: translateX(24px); transform: translateX(24px);
background: white; background: white;
} }
@@ -755,7 +772,6 @@ body::before {
} }
@keyframes pulse { @keyframes pulse {
0%, 0%,
100% { 100% {
opacity: 1; opacity: 1;
@@ -797,3 +813,56 @@ body::before {
.hidden { .hidden {
display: none !important; 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);
}

28
update-version.js Normal file
View File

@@ -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);
}
}