dev-Rachitgarg commited on
Commit
99b2ed4
·
verified ·
1 Parent(s): 06f7fcc

You are a senior front-end UI/UX engineer.

Browse files

Your task is to recreate and *visually improve* my entire UI while keeping all
functionalities, IDs, events, and JavaScript compatibility intact.

🎯 GOALS
- Make my UI look 10× more modern, clean, premium, and professional.
- Use a futuristic glass-morphism + neon cyber-dashboard style.
- Improve spacing, alignment, contrast, color palette, and readability.
- Make every widget (cards, gauges, charts, logs, MQTT indicators, sliders, buttons)
look highly polished.
- Keep all functionality identical.
- Keep ALL element IDs exactly the same because my JS depends on them.
- You may reorganize layout using grid/flex for better design.
- Add smooth animations, hover effects, glow effects, blurred backgrounds.
- Add consistent fonts and improve typography.
- Keep everything fully responsive for all screen sizes.
- Final output must give:
1. index.html
2. style.css
3. (optional) small UI-animation improvements in JS (no logic changes)

⚠️ IMPORTANT RULES
- DO NOT modify or remove any existing IDs.
- DO NOT change the JavaScript logic flow.
- Only redesign the VISUAL UI.
- You can wrap elements in new containers to improve layout.

🧠 HERE IS MY CURRENT CODE (HTML + CSS + JS).
Use this ONLY as structure reference.
Redesign it completely into a polished, beautiful UI:

-----------------------------------
PASTE START
-----------------------------------

/* PASTE YOUR FULL CODE HERE */

/* app.js — Full AirSense dashboard logic (HiveMQ WSS + gauges + conversion + alarms)
Requirements:
- gauges.js must be loaded first (defines window.Gauge)
- This file will dynamically load mqtt.min.js (mqtt.js browser bundle) if not already present
- Exposes window._AirSense for debugging
*/

/* ================== CONFIG ================== */
const CONFIG = {
// HiveMQ Cloud WSS (browser websocket)
MQTT: {
HOST_WS: 'wss://26b1337297f546aca14cdeed96ef0d24.s1.eu.hivemq.cloud:8884/mqtt',
USER: 'vikhyatesp32',
PASS: 'Vikhyat1234',
TOPIC: 'airsense/vikhy/device1/data',
CLIENT_PREFIX: 'dash_browser_'
},

// Gauge definitions & thresholds (ppm)
GAUGES: {
co2: { label: 'CO₂', unit: 'ppm', min: 0, max: 5000, ranges: { safe: 400, warn: 1000 } }, // user: 400/1000/2000
co: { label: 'CO', unit: 'ppm', min: 0, max: 1000, ranges: { safe: 50, warn: 300 } }, // user: 50/300/700
voc: { label: 'VOC', unit: 'ppm', min: 0, max: 2000, ranges: { safe: 220, warn: 400 } },
smoke:{ label: 'Smoke',unit:'ppm', min: 0, max: 2000, ranges: { safe: 150, warn: 300 } }
},

UI_THROTTLE_MS: 500, // throttle UI updates
SIREN_ON_CRITICAL: true,
OFFLINE_TIMEOUT_MS: 10000,
BATTERY_CONSUMPTION_MAH: 40,
CHART_MAX_DATA_POINTS: 50 // Max points to show on chart
};

/* ================== STATE ================== */
const state = {
mqttClient: null,
mqttConnected: false,
devices: {},
selectedDeviceId: null,
lastUIUpdate: 0,
forcedAlarm: null,
sirenPlaying: false,
sirenNodes: null,
aqiChart: null // To hold the chart instance
};

/* ================== DOM refs ================== */
const dom = {
connRibbon: document.getElementById('connRibbon'),
deviceSelect: document.getElementById('deviceSelect'),
deviceId: document.getElementById('deviceId'),
lastUpdate: document.getElementById('lastUpdate'),
batteryPercent: document.getElementById('batteryPercent'),
batteryLevelBar: document.getElementById('batteryLevelBar'),
hoursLeft: document.getElementById('hoursLeft'),
deviceStatus: document.getElementById('deviceStatus'),
deviceAlarm: document.getElementById('deviceAlarm'),
deviceSignal: document.getElementById('deviceSignal'),
sampleRate: document.getElementById('sampleRate'),
eventsList: document.getElementById('eventsList'),
ledGreen: document.getElementById('ledGreen'),
ledYellow: document.getElementById('ledYellow'),
ledRed: document.getElementById('ledRed'),
ledGreenWrap: document.getElementById('ledGreenWrap'),
ledYellowWrap: document.getElementById('ledYellowWrap'),
ledRedWrap: document.getElementById('ledRedWrap'),
fullscreenAlarm: document.getElementById('fullscreenAlarm'),
alarmDesc: document.getElementById('alarmDesc'),
alarmStop: document.getElementById('alarmStop'),
alarmAck: document.getElementById('alarmAck'),
alarmToggle: document.getElementById('alarmToggle'),
testAlarmBtn: document.getElementById('testAlarmBtn'),

// Chart canvas
aqiHistoryChart: document.getElementById('aqiHistoryChart'),

// Export Button
exportDataBtn: document.getElementById('exportDataBtn')
};

/* ================== Load MQTT lib dynamically if needed ================== */
function ensureMqttLib() {
return new Promise((resolve, reject) => {
if (window.mqtt) return resolve(window.mqtt);
const url = 'https://unpkg.com/mqtt/dist/mqtt.min.js';
const s = document.createElement('script');
s.src = url;
s.async = true;
s.onload = () => {
if (window.mqtt) resolve(window.mqtt);
else reject(new Error('mqtt lib loaded but window.mqtt missing'));
};
s.onerror = (e) => reject(new Error('Failed to load mqtt lib: ' + e));
document.head.appendChild(s);
});
}

/* ================== Gauges init ================== */
const gaugeContainers = {
co2: document.getElementById('gauge-co2'),
co: document.getElementById('gauge-co'),
voc: document.getElementById('gauge-voc'),
smoke: document.getElementById('gauge-smoke')
};
const gauges = {};
Object.keys(CONFIG.GAUGES).forEach(k => {
const cfg = CONFIG.GAUGES[k];
gauges[k] = new Gauge(gaugeContainers[k], {
label: cfg.label, unit: cfg.unit, min: cfg.min, max: cfg.max, ranges: cfg.ranges,
width: 300, height: 300,
colors: { safe: '#2ecc71', warn: '#e67e22', danger: '#e74c3c', track: 'rgba(255,255,255,0.04)' }
});
});

/* ================== Helpers ================== */
function pushEvent(txt) {
// Gracefully handle if eventsList is missing
if (!dom.eventsList) {
console.log("Event:", txt);
return;
}
const li = document.createElement('li');
const now = new Date();
li.innerHTML = `<div><strong>${txt}</strong><div class="time muted">${now.toLocaleString()}</div></div>`;
dom.eventsList.prepend(li);
while (dom.eventsList.children.length > 60) dom.eventsList.removeChild(dom.eventsList.lastChild);
}

function shouldUpdateUI() {
if (Date.now() - state.lastUIUpdate >= CONFIG.UI_THROTTLE_MS) {
state.lastUIUpdate = Date.now();
return true;
}
return false;
}

function valueToPpmFromVoltage(sensorKey, volts) {
// Basic conversion heuristics (approximate). You told me earlier conversions may be done in website.
// These are linear scalings chosen to map expected voltage ranges to ppm ranges used by gauges above.
// If you have calibration data replace these formulas with accurate mappings.
if (!isFinite(volts)) return 0;
switch (sensorKey) {
case 'voc': // MQ-2 -> VOC (combined)
// assume 0..5V -> 0..2000 ppm
return (volts / 5.0) * 2000;
case 'smoke': // MQ-2 + MQ-3 assist -> smoke ppm
return (volts / 5.0) * 2000;
case 'co': // MQ-7
// assume 0..5V -> 0..1000 ppm (MQ-7 often lower but use wider range)
return (volts / 5.0) * 1000;
case 'co2': // MQ-135
// assume 0..5V -> 0..5000 ppm
return (volts / 5.0) * 5000;
default:
return (volts / 5.0) * 1000;
}
}

function severityFromGases(gases) {
// returns 'none' | 'warning' | 'critical'
let sev = 0;
if (!gases) return 'none';
// CO thresholds (user provided)
const co = Number(gases.co ?? 0);
if (co > 700) sev = Math.max(sev, 2);
else if (co > 300) sev = Math.max(sev, 1);

// CO2 thresholds (user provided)
const co2 = Number(gases.co2 ?? 0);
if (co2 > 2000) sev = Math.max(sev, 2);
else if (co2 > 1000) sev = Math.max(sev, 1);

// VOC / Smoke thresholds - use gauge ranges (approx)
const voc = Number(gases.voc ?? 0);
if (voc > (CONFIG.GAUGES.voc.ranges?.warn || 400)) sev = Math.max(sev, 1);
// treat high voc > 1200 as critical
if (voc > 1200) sev = Math.max(sev, 2);

const smoke = Number(gases.smoke ?? 0);
if (smoke > (CONFIG.GAUGES.smoke.ranges?.warn || 300)) sev = Math.max(sev, 1);
if (smoke > 1200) sev = Math.max(sev, 2);

return sev === 2 ? 'critical' : (sev === 1 ? 'warning' : 'none');
}

/* LED visuals */
function clearLEDs() { [dom.ledGreen, dom.ledYellow, dom.ledRed].forEach(el => el.classList.remove('led-active-green','led-active-yellow','led-active-red','led-pulse')); }
function setLEDState(level) {
clearLEDs();
if (state.forcedAlarm) level = state.forcedAlarm;
let pulseSpeed = 1.6;
if (level === 'none') { dom.ledGreen.classList.add('led-active-green', 'led-pulse'); pulseSpeed = 2.4; }
else if (level === 'warning') { dom.ledYellow.classList.add('led-active-yellow', 'led-pulse'); pulseSpeed = 1.1; }
else if (level === 'critical') { dom.ledRed.classList.add('led-active-red', 'led-pulse'); pulseSpeed = 0.6; }
[dom.ledGreen, dom.ledYellow, dom.ledRed].forEach(el => { if (el.classList.contains('led-pulse')) el.style.animationDuration = `${pulseSpeed}s`; });
}

/* Battery UI (keeps simple) */
function updateBatteryUI(battPercent) {
dom.batteryPercent.textContent = `${battPercent}%`;
dom.batteryLevelBar.style.width = `${battPercent}%`;
if (battPercent > 50) dom.batteryLevelBar.style.background = 'linear-gradient(90deg,var(--green), #28b463)';
else if (battPercent > 20) dom.batteryLevelBar.style.background = 'linear-gradient(90deg,var(--orange), #f39c12)';
else dom.batteryLevelBar.style.background = 'linear-gradient(90deg,var(--red), #e74c3c)';

const consumption = CONFIG.BATTERY_CONSUMPTION_MAH || 40;
const hoursLeft = ((battPercent/100) * 1000) / consumption;
dom.hoursLeft.textContent = `${Math.max(0, Math.round(hoursLeft*10)/10)} hrs left`;
}

/* ================== NEW: AQI Calculation ================== */

/**
* Maps a value from one range to an AQI-like index (0-500).
* "warn" threshold maps to 100.
* "max" threshold maps to 500.
*/
function mapToSubIndex(value, warn, max) {
const v = Number(value) || 0;
const w = Number(warn) || 1; // Avoid division by zero
const m

Files changed (3) hide show
  1. README.md +8 -5
  2. index.html +232 -19
  3. style.css +167 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Airsense Cyberdash 3000
3
- emoji: 😻
4
- colorFrom: green
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: AirSense CyberDash 3000 🚀
3
+ colorFrom: blue
4
+ colorTo: yellow
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
index.html CHANGED
@@ -1,19 +1,232 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AirSense CyberDash 3000</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://unpkg.com/feather-icons"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
11
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&family=Orbitron:wght@400;600&display=swap" rel="stylesheet">
12
+ <script src="components/navbar.js"></script>
13
+ <script src="components/footer.js"></script>
14
+ </head>
15
+ <body class="bg-gray-900 text-white font-sans">
16
+ <custom-navbar></custom-navbar>
17
+
18
+ <main class="container mx-auto px-4 py-8 grid grid-cols-1 lg:grid-cols-12 gap-6">
19
+ <!-- Left Column -->
20
+ <div class="lg:col-span-3 space-y-6">
21
+ <!-- Device Card -->
22
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white">
23
+ <h2 class="text-xl font-bold mb-4 flex items-center">
24
+ <i data-feather="cpu" class="mr-2"></i> Device Status
25
+ </h2>
26
+ <div class="space-y-4">
27
+ <div>
28
+ <p class="text-sm text-gray-400">Device ID</p>
29
+ <p id="deviceId" class="text-lg font-mono">No device connected</p>
30
+ </div>
31
+ <div>
32
+ <p class="text-sm text-gray-400">Last Update</p>
33
+ <p id="lastUpdate" class="text-sm">Waiting for data</p>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="mt-6">
38
+ <div class="flex justify-between items-center mb-1">
39
+ <span class="text-sm text-gray-400">Battery</span>
40
+ <span id="batteryPercent" class="text-sm">—%</span>
41
+ </div>
42
+ <div class="w-full bg-gray-800 rounded-full h-2.5">
43
+ <div id="batteryLevelBar" class="h-2.5 rounded-full bg-gradient-to-r from-green-400 to-blue-500" style="width: 0%"></div>
44
+ </div>
45
+ <p id="hoursLeft" class="text-xs text-gray-400 mt-1">— hrs left</p>
46
+ </div>
47
+
48
+ <div class="grid grid-cols-2 gap-4 mt-6">
49
+ <div>
50
+ <p class="text-sm text-gray-400">Status</p>
51
+ <p id="deviceStatus" class="text-sm">idle</p>
52
+ </div>
53
+ <div>
54
+ <p class="text-sm text-gray-400">Alarm</p>
55
+ <p id="deviceAlarm" class="text-sm">none</p>
56
+ </div>
57
+ <div>
58
+ <p class="text-sm text-gray-400">Signal</p>
59
+ <p id="deviceSignal" class="text-sm">—</p>
60
+ </div>
61
+ <div>
62
+ <p class="text-sm text-gray-400">Sample</p>
63
+ <p id="sampleRate" class="text-sm">—</p>
64
+ </div>
65
+ </div>
66
+
67
+ <div class="flex space-x-2 mt-6">
68
+ <button id="alarmToggle" class="flex-1 bg-gradient-to-r from-purple-600 to-blue-500 hover:from-purple-700 hover:to-blue-600 text-white py-2 px-4 rounded-lg transition-all disabled:opacity-50" disabled>
69
+ Alarm: OFF
70
+ </button>
71
+ <button id="testAlarmBtn" class="bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white py-2 px-4 rounded-lg transition-all disabled:opacity-50" disabled>
72
+ Test
73
+ </button>
74
+ </div>
75
+ </div>
76
+
77
+ <!-- LED Indicators -->
78
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white">
79
+ <h2 class="text-xl font-bold mb-4 flex items-center">
80
+ <i data-feather="alert-triangle" class="mr-2"></i> Status Indicators
81
+ </h2>
82
+ <div class="flex justify-between">
83
+ <div id="ledGreenWrap" class="text-center cursor-pointer">
84
+ <div id="ledGreen" class="led led-green mx-auto"></div>
85
+ <p class="text-sm mt-2">OK</p>
86
+ </div>
87
+ <div id="ledYellowWrap" class="text-center cursor-pointer">
88
+ <div id="ledYellow" class="led led-yellow mx-auto"></div>
89
+ <p class="text-sm mt-2">Warning</p>
90
+ </div>
91
+ <div id="ledRedWrap" class="text-center cursor-pointer">
92
+ <div id="ledRed" class="led led-red mx-auto"></div>
93
+ <p class="text-sm mt-2">Critical</p>
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ <!-- Events Log -->
99
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white h-96 overflow-hidden">
100
+ <h2 class="text-xl font-bold mb-4 flex items-center">
101
+ <i data-feather="activity" class="mr-2"></i> Event Log
102
+ </h2>
103
+ <ul id="eventsList" class="space-y-2 overflow-y-auto h-80 pr-2">
104
+ <!-- Events will be added here dynamically -->
105
+ </ul>
106
+ </div>
107
+ </div>
108
+
109
+ <!-- Middle Column -->
110
+ <div class="lg:col-span-6 space-y-6">
111
+ <!-- Gauges Grid -->
112
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
113
+ <div id="gauge-co2" class="glass-card p-4 rounded-xl backdrop-blur-lg border border-opacity-20 border-white"></div>
114
+ <div id="gauge-co" class="glass-card p-4 rounded-xl backdrop-blur-lg border border-opacity-20 border-white"></div>
115
+ <div id="gauge-voc" class="glass-card p-4 rounded-xl backdrop-blur-lg border border-opacity-20 border-white"></div>
116
+ <div id="gauge-smoke" class="glass-card p-4 rounded-xl backdrop-blur-lg border border-opacity-20 border-white"></div>
117
+ </div>
118
+
119
+ <!-- AQI Chart -->
120
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white">
121
+ <div class="flex justify-between items-center mb-4">
122
+ <h2 class="text-xl font-bold flex items-center">
123
+ <i data-feather="bar-chart-2" class="mr-2"></i> AQI History
124
+ </h2>
125
+ <button id="exportDataBtn" class="text-sm bg-gray-800 hover:bg-gray-700 px-3 py-1 rounded-lg transition-all">
126
+ Export Data
127
+ </button>
128
+ </div>
129
+ <div class="h-64">
130
+ <canvas id="aqiHistoryChart"></canvas>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Right Column -->
136
+ <div class="lg:col-span-3 space-y-6">
137
+ <!-- Connection Status -->
138
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white">
139
+ <h2 class="text-xl font-bold mb-4 flex items-center">
140
+ <i data-feather="wifi" class="mr-2"></i> Connection
141
+ </h2>
142
+ <div class="flex items-center justify-between mb-4">
143
+ <span class="text-sm">MQTT Status</span>
144
+ <span id="connRibbon" class="px-3 py-1 rounded-full text-xs font-medium bg-gray-800">Idle</span>
145
+ </div>
146
+ <div class="mt-6">
147
+ <label for="deviceSelect" class="block text-sm text-gray-400 mb-2">Select Device</label>
148
+ <select id="deviceSelect" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
149
+ <option value="">No device</option>
150
+ </select>
151
+ </div>
152
+ </div>
153
+
154
+ <!-- Quick Actions -->
155
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white">
156
+ <h2 class="text-xl font-bold mb-4 flex items-center">
157
+ <i data-feather="zap" class="mr-2"></i> Quick Actions
158
+ </h2>
159
+ <div class="space-y-3">
160
+ <button id="testAlarmBtn" class="w-full bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white py-2 px-4 rounded-lg transition-all disabled:opacity-50" disabled>
161
+ Test Alarm
162
+ </button>
163
+ <button class="w-full bg-gray-800 hover:bg-gray-700 text-white py-2 px-4 rounded-lg transition-all">
164
+ Force Refresh
165
+ </button>
166
+ <button class="w-full bg-gray-800 hover:bg-gray-700 text-white py-2 px-4 rounded-lg transition-all">
167
+ Settings
168
+ </button>
169
+ </div>
170
+ </div>
171
+
172
+ <!-- System Info -->
173
+ <div class="glass-card p-6 rounded-xl backdrop-blur-lg border border-opacity-20 border-white">
174
+ <h2 class="text-xl font-bold mb-4 flex items-center">
175
+ <i data-feather="info" class="mr-2"></i> System Info
176
+ </h2>
177
+ <div class="space-y-2 text-sm">
178
+ <div class="flex justify-between">
179
+ <span class="text-gray-400">Version</span>
180
+ <span>v3.0.0</span>
181
+ </div>
182
+ <div class="flex justify-between">
183
+ <span class="text-gray-400">Last Sync</span>
184
+ <span>Just now</span>
185
+ </div>
186
+ <div class="flex justify-between">
187
+ <span class="text-gray-400">Uptime</span>
188
+ <span>00:12:45</span>
189
+ </div>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ </main>
194
+
195
+ <!-- Alarm Modal -->
196
+ <div id="fullscreenAlarm" class="fixed inset-0 z-50 flex items-center justify-center hidden" aria-hidden="true">
197
+ <div class="absolute inset-0 bg-black bg-opacity-70 backdrop-blur-sm"></div>
198
+ <div class="relative bg-gradient-to-br from-red-900 to-red-800 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl border border-red-500 border-opacity-50">
199
+ <div class="text-center">
200
+ <div class="flex justify-center mb-4">
201
+ <div class="p-3 bg-red-500 bg-opacity-20 rounded-full">
202
+ <i data-feather="alert-octagon" class="w-12 h-12 text-red-400"></i>
203
+ </div>
204
+ </div>
205
+ <h2 id="alarmTitle" class="text-2xl font-bold mb-2">CRITICAL ALERT</h2>
206
+ <div id="alarmIndicator" class="inline-block px-3 py-1 mb-4 text-sm font-semibold rounded-full bg-red-500 text-white">CRITICAL</div>
207
+ <p id="alarmDesc" class="text-gray-300 mb-6">Dangerous gas levels detected. Stop or acknowledge to silence the siren.</p>
208
+ <div class="flex space-x-3 justify-center">
209
+ <button id="alarmStop" class="px-6 py-2 bg-red-600 hover:bg-red-700 rounded-lg transition-all">Stop Alarm</button>
210
+ <button id="alarmAck" class="px-6 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg transition-all">Acknowledge</button>
211
+ </div>
212
+ </div>
213
+ </div>
214
+ </div>
215
+
216
+ <custom-footer></custom-footer>
217
+
218
+ <script src="gauges.js"></script>
219
+ <script src="app.js"></script>
220
+ <script>
221
+ feather.replace();
222
+ document.addEventListener('DOMContentLoaded', () => {
223
+ // Initialize tooltips
224
+ const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
225
+ tooltipTriggerList.map(function (tooltipTriggerEl) {
226
+ return new bootstrap.Tooltip(tooltipTriggerEl);
227
+ });
228
+ });
229
+ </script>
230
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
231
+ </body>
232
+ </html>
style.css CHANGED
@@ -1,28 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&family=Orbitron:wght@400;600&display=swap');
2
+
3
+ :root {
4
+ --primary: #6366f1;
5
+ --primary-dark: #4f46e5;
6
+ --secondary: #10b981;
7
+ --danger: #ef4444;
8
+ --warning: #f59e0b;
9
+ --success: #10b981;
10
+ --info: #3b82f6;
11
+ --dark: #1e293b;
12
+ --light: #f8fafc;
13
+ --muted: #64748b;
14
+ }
15
+
16
  body {
17
+ font-family: 'Poppins', sans-serif;
18
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
19
+ min-height: 100vh;
20
+ color: white;
21
+ }
22
+
23
+ .glass-card {
24
+ background: rgba(15, 23, 42, 0.7);
25
+ backdrop-filter: blur(10px);
26
+ -webkit-backdrop-filter: blur(10px);
27
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.1);
28
+ transition: all 0.3s ease;
29
+ }
30
+
31
+ .glass-card:hover {
32
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
33
+ transform: translateY(-2px);
34
+ }
35
+
36
+ .led {
37
+ width: 24px;
38
+ height: 24px;
39
+ border-radius: 50%;
40
+ position: relative;
41
+ transition: all 0.3s ease;
42
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0);
43
+ }
44
+
45
+ .led-green {
46
+ background-color: #10b981;
47
+ }
48
+
49
+ .led-yellow {
50
+ background-color: #f59e0b;
51
+ }
52
+
53
+ .led-red {
54
+ background-color: #ef4444;
55
+ }
56
+
57
+ .led-active-green {
58
+ box-shadow: 0 0 15px #10b981;
59
+ }
60
+
61
+ .led-active-yellow {
62
+ box-shadow: 0 0 15px #f59e0b;
63
+ }
64
+
65
+ .led-active-red {
66
+ box-shadow: 0 0 15px #ef4444;
67
+ }
68
+
69
+ .led-pulse {
70
+ animation: pulse 1.5s infinite;
71
+ }
72
+
73
+ @keyframes pulse {
74
+ 0% {
75
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7);
76
+ }
77
+ 70% {
78
+ box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
79
+ }
80
+ 100% {
81
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
82
+ }
83
+ }
84
+
85
+ .gauge-wrapper {
86
+ position: relative;
87
+ text-align: center;
88
  }
89
 
90
+ .gauge-readout {
91
+ margin-top: 1rem;
 
92
  }
93
 
94
+ .gauge-value {
95
+ font-size: 1.5rem;
96
+ font-weight: bold;
97
+ font-family: 'Orbitron', sans-serif;
 
98
  }
99
 
100
+ .gauge-unit {
101
+ font-size: 0.9rem;
102
+ color: var(--muted);
 
 
 
103
  }
104
 
105
+ #eventsList {
106
+ max-height: 300px;
107
+ overflow-y: auto;
108
  }
109
+
110
+ #eventsList li {
111
+ padding: 0.5rem 0;
112
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
113
+ }
114
+
115
+ #eventsList li:first-child {
116
+ padding-top: 0;
117
+ }
118
+
119
+ #eventsList li:last-child {
120
+ border-bottom: none;
121
+ }
122
+
123
+ #fullscreenAlarm {
124
+ transition: all 0.3s ease;
125
+ }
126
+
127
+ /* Responsive adjustments */
128
+ @media (max-width: 1024px) {
129
+ .glass-card {
130
+ padding: 1.5rem;
131
+ }
132
+ }
133
+
134
+ @media (max-width: 768px) {
135
+ .grid-cols-2 {
136
+ grid-template-columns: 1fr;
137
+ }
138
+
139
+ .glass-card {
140
+ padding: 1rem;
141
+ }
142
+ }
143
+
144
+ /* Custom scrollbar */
145
+ ::-webkit-scrollbar {
146
+ width: 8px;
147
+ }
148
+
149
+ ::-webkit-scrollbar-track {
150
+ background: rgba(255, 255, 255, 0.05);
151
+ }
152
+
153
+ ::-webkit-scrollbar-thumb {
154
+ background: rgba(255, 255, 255, 0.2);
155
+ border-radius: 4px;
156
+ }
157
+
158
+ ::-webkit-scrollbar-thumb:hover {
159
+ background: rgba(255, 255, 255, 0.3);
160
+ }
161
+
162
+ /* Animations */
163
+ @keyframes fadeIn {
164
+ from { opacity: 0; transform: translateY(10px); }
165
+ to { opacity: 1; transform: translateY(0); }
166
+ }
167
+
168
+ .animate-fade-in {
169
+ animation: fadeIn 0.5s ease forwards;
170
+ }
171
+
172
+ /* Glow effects */
173
+ .glow-primary {
174
+ box-shadow: 0 0 15px rgba(99, 102, 241, 0.5);
175
+ }
176
+
177
+ .g