@sailingrotevista/rotevista-dash 4.0.15 → 4.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +124 -69
- package/package.json +1 -1
- package/style.css +117 -194
package/app.js
CHANGED
|
@@ -12,21 +12,21 @@
|
|
|
12
12
|
// 1. CONFIGURAZIONE E DEFAULT
|
|
13
13
|
// ==========================================================================
|
|
14
14
|
let CONFIG = {
|
|
15
|
-
alarms: { depthDanger: 2.5, depthWarning: 5
|
|
15
|
+
alarms: { depthDanger: 2.5, depthWarning: 3.5 },
|
|
16
16
|
averaging: {
|
|
17
17
|
smoothWindow: 2000,
|
|
18
18
|
longWindow: 30000,
|
|
19
19
|
stabilityTolerance: 2000,
|
|
20
20
|
stabilityThreshold: 0.85,
|
|
21
|
-
minSpeed: 0
|
|
21
|
+
minSpeed: 0,
|
|
22
22
|
stabilityBreakout: 15
|
|
23
23
|
},
|
|
24
|
-
graphs: { reef1:
|
|
24
|
+
graphs: { reef1: 8, reef2: 10.0, historyMinutes: 30, samples: 60 },
|
|
25
25
|
scales: {
|
|
26
|
-
stw: { stdMax:
|
|
27
|
-
sog: { stdMax:
|
|
28
|
-
tws: { stdMax:
|
|
29
|
-
depth: { stdMax:
|
|
26
|
+
stw: { stdMax: 8, hercSpan: 4, step: 2 },
|
|
27
|
+
sog: { stdMax: 8, hercSpan: 4, step: 2 },
|
|
28
|
+
tws: { stdMax: 15, hercSpan: 10, step: 5 },
|
|
29
|
+
depth: { stdMax: 8, hercSpan: 5, step: 5 }
|
|
30
30
|
},
|
|
31
31
|
server: { fallbackIp: "192.168.111.240:3000" }
|
|
32
32
|
};
|
|
@@ -241,9 +241,16 @@ function playGybeAlarm() {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
function checkDepthAlarm(m) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
244
|
+
// Rimuoviamo sempre le classi prima di riapplicarle
|
|
245
|
+
ui.depth.classList.remove('alarm-warning', 'alarm-danger', 'blink-alarm');
|
|
246
|
+
|
|
247
|
+
// Logica di confronto dinamica
|
|
248
|
+
if (m < CONFIG.alarms.depthDanger) {
|
|
249
|
+
ui.depth.classList.add('alarm-danger', 'blink-alarm');
|
|
250
|
+
playBingBing(); // Il tuo suono di allarme
|
|
251
|
+
} else if (m < CONFIG.alarms.depthWarning) {
|
|
252
|
+
ui.depth.classList.add('alarm-warning');
|
|
253
|
+
}
|
|
247
254
|
}
|
|
248
255
|
|
|
249
256
|
function updateLeewayDisplay(deg) {
|
|
@@ -430,19 +437,21 @@ function startDisplayLoop() {
|
|
|
430
437
|
const labelEl = document.getElementById('sog-vmg-label');
|
|
431
438
|
if (displayModeSog === 'VMG') {
|
|
432
439
|
ui.sog.innerText = vmg.toFixed(1);
|
|
433
|
-
|
|
440
|
+
// AGGIORNATO AL NUOVO COLORE CYAN VIBRANTE (#00b8d4)
|
|
441
|
+
ui.sog.style.setProperty('color', '#00b8d4', 'important');
|
|
434
442
|
if (labelEl) labelEl.textContent = 'VMG';
|
|
435
443
|
} else {
|
|
436
444
|
ui.sog.innerText = sogKts.toFixed(1);
|
|
437
445
|
if (labelEl) labelEl.textContent = 'SOG';
|
|
438
446
|
|
|
439
|
-
// Colore Corrente:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
else ui.sog.style.color
|
|
447
|
+
// Colore Corrente: Giallo Caldo per corrente a favore, Rosso per corrente contraria
|
|
448
|
+
// (Allineiamo il colore della corrente a favore con il #ffbb33 usato nei grafici)
|
|
449
|
+
if (sogKts - stwKts > 0.3) ui.sog.style.setProperty('color', '#ffbb33', 'important');
|
|
450
|
+
else if (sogKts - stwKts < -0.3) ui.sog.style.setProperty('color', '#ff3b30', 'important'); // Rosso vivido
|
|
451
|
+
else ui.sog.style.color = ""; // Torna normale
|
|
443
452
|
}
|
|
444
453
|
}
|
|
445
|
-
|
|
454
|
+
|
|
446
455
|
if (store.raw["environment.depth.belowTransducer"] !== undefined) {
|
|
447
456
|
ui.depth.innerText = store.raw["environment.depth.belowTransducer"].toFixed(1);
|
|
448
457
|
checkDepthAlarm(store.raw["environment.depth.belowTransducer"]);
|
|
@@ -552,41 +561,25 @@ function startDisplayLoop() {
|
|
|
552
561
|
async function fetchServerConfig() {
|
|
553
562
|
try {
|
|
554
563
|
const response = await fetch('/rotevista-config');
|
|
555
|
-
if (!response.ok) throw new Error(`
|
|
564
|
+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
556
565
|
const data = await response.json();
|
|
557
566
|
|
|
558
|
-
//
|
|
559
|
-
|
|
560
|
-
for (let k in obj) {
|
|
561
|
-
if (typeof obj[k] === 'object' && obj[k] !== null) parse(obj[k]);
|
|
562
|
-
else if (!isNaN(obj[k]) && typeof obj[k] === 'string' && obj[k] !== "")
|
|
563
|
-
obj[k] = parseFloat(obj[k]);
|
|
564
|
-
}
|
|
565
|
-
return obj;
|
|
566
|
-
};
|
|
567
|
-
const actual = parse(data);
|
|
567
|
+
// Stampa di debug per verificare cosa riceve il client
|
|
568
|
+
console.log("🔍 Configurazione ricevuta dal Server:", data);
|
|
568
569
|
|
|
569
|
-
//
|
|
570
|
-
|
|
570
|
+
// Merge intelligente dei dati ricevuti nel CONFIG esistente
|
|
571
|
+
Object.assign(CONFIG.alarms, data.alarms || {});
|
|
572
|
+
Object.assign(CONFIG.graphs, data.graphs || {});
|
|
573
|
+
Object.assign(CONFIG.averaging, data.averaging || {});
|
|
571
574
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
if (actual.averaging) {
|
|
578
|
-
Object.assign(CONFIG.averaging, actual.averaging);
|
|
579
|
-
console.log(`⏱️ STABILITÀ: MinSpeed ${CONFIG.averaging.minSpeed}kts | Breakout: ${CONFIG.averaging.stabilityBreakout}°`);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (actual.scales) {
|
|
583
|
-
// Per le scale facciamo un merge profondo per ogni box
|
|
584
|
-
for (let key in actual.scales) {
|
|
585
|
-
if (CONFIG.scales[key]) Object.assign(CONFIG.scales[key], actual.scales[key]);
|
|
575
|
+
// Per le scale, siccome sono nidificate, facciamo un loop
|
|
576
|
+
if (data.scales) {
|
|
577
|
+
for (let key in data.scales) {
|
|
578
|
+
if (CONFIG.scales[key]) Object.assign(CONFIG.scales[key], data.scales[key]);
|
|
586
579
|
}
|
|
587
580
|
}
|
|
588
581
|
|
|
589
|
-
console.log("✅ Configurazione
|
|
582
|
+
console.log("✅ Configurazione applicata. Alarmi attivi:", CONFIG.alarms);
|
|
590
583
|
} catch (err) {
|
|
591
584
|
console.warn("⚠️ Utilizzo default locali. Motivo:", err.message);
|
|
592
585
|
}
|
|
@@ -621,46 +614,108 @@ function updateScaleLabels(t, min, max) {
|
|
|
621
614
|
|
|
622
615
|
/**
|
|
623
616
|
* drawGraph: Disegna i grafici con griglia temporale intelligente
|
|
617
|
+
* Usa un Gradiente Lineare SVG dinamico per eliminare le giunzioni dei poligoni.
|
|
624
618
|
*/
|
|
625
619
|
function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
626
|
-
const svg = document.getElementById(id);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
620
|
+
const svg = document.getElementById(id);
|
|
621
|
+
if (!svg || d.length < 2) return;
|
|
622
|
+
|
|
623
|
+
const w = 200, h = 40;
|
|
624
|
+
const range = max - min || 1;
|
|
625
|
+
const isDepth = (id === 'depth-graph');
|
|
626
|
+
const samples = CONFIG.graphs.samples;
|
|
627
|
+
|
|
628
|
+
// 1. Griglia
|
|
630
629
|
let grids = "";
|
|
631
630
|
[0.25, 0.5, 0.75].forEach(p => {
|
|
632
631
|
grids += `<line x1="0" y1="${h-(p*h)}" x2="${w}" y2="${h-(p*h)}" stroke="rgba(0,0,0,0.12)" stroke-width="0.5" />`;
|
|
633
632
|
});
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* Griglia Temporale Intelligente:
|
|
637
|
-
* - Storia <= 15 min: linee ogni 1 minuto.
|
|
638
|
-
* - Storia > 15 min: linee ogni 5 minuti.
|
|
639
|
-
*/
|
|
640
633
|
const gridInterval = (CONFIG.graphs.historyMinutes <= 15) ? 1 : 5;
|
|
641
|
-
|
|
642
634
|
for (let m = gridInterval; m < CONFIG.graphs.historyMinutes; m += gridInterval) {
|
|
643
635
|
const x = w - (m / CONFIG.graphs.historyMinutes) * w;
|
|
644
636
|
grids += `<line x1="${x}" y1="0" x2="${x}" y2="${h}" stroke="rgba(0,0,0,0.08)" stroke-width="0.5" />`;
|
|
645
637
|
}
|
|
646
638
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
639
|
+
// 2. Colori (Tavolozza Vivida)
|
|
640
|
+
const baseColorTws = "#2c3e50";
|
|
641
|
+
const baseColorDepth = "#0088cc";
|
|
642
|
+
|
|
643
|
+
const getColor = (val) => {
|
|
644
|
+
if (isTws) return (val >= CONFIG.graphs.reef2) ? "#ff3b30" : (val >= CONFIG.graphs.reef1 ? "#ff9800" : baseColorTws);
|
|
645
|
+
if (isDepth) return (val < CONFIG.alarms.depthDanger) ? "#ff3b30" : (val < CONFIG.alarms.depthWarning ? "#ff9800" : baseColorDepth);
|
|
646
|
+
switch(id) {
|
|
647
|
+
case 'stw-graph': return "#00C851";
|
|
648
|
+
case 'sog-graph': return (displayModeSog === 'VMG') ? "#00b8d4" : "#ffbb33";
|
|
649
|
+
default: return baseColorTws;
|
|
655
650
|
}
|
|
656
|
-
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// 3. Creazione Gradiente e Linee
|
|
654
|
+
let gradientStops = "";
|
|
655
|
+
let lines = "";
|
|
656
|
+
let areaPath = `M 0 ${h} `;
|
|
657
|
+
|
|
658
|
+
for (let i = 1; i < d.length; i++) {
|
|
659
|
+
// Calcolo Percentuali per il gradiente
|
|
660
|
+
const percentPrev = ((i - 1) / (samples - 1)) * 100;
|
|
661
|
+
const percentCurr = (i / (samples - 1)) * 100;
|
|
662
|
+
|
|
663
|
+
// Coordinate geometriche
|
|
664
|
+
const x1 = ((i - 1) / (samples - 1)) * w;
|
|
665
|
+
const y1 = h - (Math.max(0, Math.min(1, (d[i - 1] - min) / range)) * h);
|
|
666
|
+
const x2 = (i / (samples - 1)) * w;
|
|
667
|
+
const y2 = h - (Math.max(0, Math.min(1, (d[i] - min) / range)) * h);
|
|
668
|
+
|
|
669
|
+
const color = getColor(d[i]);
|
|
670
|
+
|
|
671
|
+
// Assegnazione specifica di Opacità e Spessore Linea
|
|
672
|
+
let fillOpacity = "0.15"; // Default per valori base
|
|
673
|
+
let strokeWidth = "1.5";
|
|
674
|
+
|
|
675
|
+
if (color === "#ff3b30") {
|
|
676
|
+
// ROSSO (Danger / Reef 2): Molto solido
|
|
677
|
+
fillOpacity = "0.85";
|
|
678
|
+
strokeWidth = "2.5";
|
|
679
|
+
} else if (color === "#ff9800") {
|
|
680
|
+
// ARANCIONE (Warning / Reef 1): Più trasparente (Velo)
|
|
681
|
+
fillOpacity = "0.45";
|
|
682
|
+
strokeWidth = "2.5"; // Manteniamo la linea spessa per leggerla bene
|
|
683
|
+
}
|
|
657
684
|
|
|
658
|
-
|
|
659
|
-
|
|
685
|
+
// GRADIENTE: Due stop alla stessa percentuale per creare uno stacco di colore netto (no sfumature)
|
|
686
|
+
gradientStops += `<stop offset="${percentPrev}%" stop-color="${color}" stop-opacity="${fillOpacity}" />`;
|
|
687
|
+
gradientStops += `<stop offset="${percentCurr}%" stop-color="${color}" stop-opacity="${fillOpacity}" />`;
|
|
688
|
+
|
|
689
|
+
// LINEE: Disegnate normalmente sopra l'area
|
|
690
|
+
lines += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}"
|
|
691
|
+
style="stroke: ${color}; stroke-width: ${strokeWidth}; stroke-linecap: round;" />`;
|
|
692
|
+
|
|
693
|
+
// AGGIORNAMENTO PATH UNICO
|
|
694
|
+
if (i === 1) areaPath += `L ${x1} ${y1} `;
|
|
695
|
+
areaPath += `L ${x2} ${y2} `;
|
|
696
|
+
}
|
|
660
697
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
698
|
+
// Chiusura del path dell'area
|
|
699
|
+
areaPath += `L ${w} ${h} Z`;
|
|
700
|
+
|
|
701
|
+
// 4. Iniezione del <defs> (Gradiente) nell'SVG
|
|
702
|
+
const gradId = `grad-${id}`;
|
|
703
|
+
const defs = `
|
|
704
|
+
<defs>
|
|
705
|
+
<linearGradient id="${gradId}" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
706
|
+
${gradientStops}
|
|
707
|
+
</linearGradient>
|
|
708
|
+
</defs>
|
|
709
|
+
`;
|
|
710
|
+
|
|
711
|
+
// 5. Render Finale
|
|
712
|
+
// Se è Depth o Tws applichiamo il gradiente, altrimenti colore standard fisso (per SOG/STW)
|
|
713
|
+
if (isTws || isDepth) {
|
|
714
|
+
svg.innerHTML = `${defs}${grids}<path d="${areaPath}" fill="url(#${gradId})" stroke="none" />${lines}`;
|
|
715
|
+
} else {
|
|
716
|
+
const standardColor = getColor(d[d.length - 1]);
|
|
717
|
+
svg.innerHTML = `${grids}<path d="${areaPath}" fill="${standardColor}" fill-opacity="0.15" stroke="none" />${lines}`;
|
|
718
|
+
}
|
|
664
719
|
}
|
|
665
720
|
|
|
666
721
|
// ==========================================================================
|
package/package.json
CHANGED
package/style.css
CHANGED
|
@@ -112,7 +112,7 @@ body {
|
|
|
112
112
|
/* --- BOX TWD (Bussola Meteo) --- */
|
|
113
113
|
.value-with-compass {
|
|
114
114
|
display: flex; flex-direction: row; justify-content: space-between;
|
|
115
|
-
align-items: center; width: 100%; height: 85%;
|
|
115
|
+
align-items: center; width: 100%; height: 85%;
|
|
116
116
|
}
|
|
117
117
|
.box-twd .mini-compass {
|
|
118
118
|
height: 85cqh !important; width: auto !important; aspect-ratio: 1/1; flex-shrink: 0;
|
|
@@ -134,6 +134,33 @@ body {
|
|
|
134
134
|
.box-tack .value.dual-val { font-size: clamp(0.9rem, 28cqh, 10cqw) !important; }
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
/* ==========================================================================
|
|
138
|
+
AGGIUNTA: Gestione Allarmi Profondità
|
|
139
|
+
========================================================================== */
|
|
140
|
+
.alarm-warning {
|
|
141
|
+
color: #e67e22 !important; /* Arancione per Warning */
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.alarm-danger {
|
|
145
|
+
color: #c0392b !important; /* Rosso per Danger */
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.blink-alarm {
|
|
149
|
+
animation: blink-depth 0.8s infinite alternate;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@keyframes blink-depth {
|
|
153
|
+
from { opacity: 1; }
|
|
154
|
+
to { opacity: 0.3; }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* Override per la Night Mode: se siamo in modalità notte,
|
|
158
|
+
il rosso deve restare ben visibile o diventare più acceso */
|
|
159
|
+
body.night-mode .alarm-danger {
|
|
160
|
+
color: #ff0000 !important;
|
|
161
|
+
text-shadow: 0 0 10px rgba(255, 0, 0, 0.8) !important;
|
|
162
|
+
}
|
|
163
|
+
|
|
137
164
|
/* ==========================================================================
|
|
138
165
|
5. TACTICAL FOCUS MODE
|
|
139
166
|
========================================================================== */
|
|
@@ -142,7 +169,7 @@ body {
|
|
|
142
169
|
|
|
143
170
|
.focus-active .is-focused .sparkline path,
|
|
144
171
|
.focus-active .is-focused .sparkline line {
|
|
145
|
-
stroke-width: 1.5px !important;
|
|
172
|
+
stroke-width: 1.5px !important;
|
|
146
173
|
}
|
|
147
174
|
|
|
148
175
|
.focus-active.focus-side-left {
|
|
@@ -208,42 +235,38 @@ body {
|
|
|
208
235
|
}
|
|
209
236
|
|
|
210
237
|
/* --- TACK: Impilamento Verticale Rigido in U-Shape --- */
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
238
|
+
.box-tack .dual-value-container {
|
|
239
|
+
flex-direction: column !important;
|
|
240
|
+
justify-content: center !important;
|
|
241
|
+
align-items: flex-start !important;
|
|
242
|
+
gap: 8px !important;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.box-tack .dual-value-col {
|
|
246
|
+
display: flex !important;
|
|
247
|
+
flex-direction: column !important;
|
|
248
|
+
align-items: flex-start !important;
|
|
249
|
+
text-align: left !important;
|
|
250
|
+
width: 100% !important;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.box-tack .value.dual-val {
|
|
254
|
+
font-size: clamp(1rem, 30cqh, 15cqw) !important;
|
|
255
|
+
line-height: 1 !important;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.box-tack .dual-label {
|
|
259
|
+
margin-bottom: 0px !important;
|
|
260
|
+
}
|
|
235
261
|
|
|
236
262
|
/* --- TWD: Calibrazione per box stretto --- */
|
|
237
263
|
.box-twd .value-with-compass { justify-content: center; gap: 5px; }
|
|
238
264
|
.box-twd .mini-compass { height: 60cqh !important; }
|
|
239
265
|
.box-twd #twd-avg { font-size: clamp(1.2rem, 50cqh, 20cqw) !important; }
|
|
240
|
-
|
|
241
|
-
/* NOTA: COG e AWA non vengono toccati qui, così restano massivi
|
|
242
|
-
ereditando la regola generale della Sezione 4 */
|
|
243
266
|
}
|
|
244
267
|
|
|
245
268
|
/* ==========================================================================
|
|
246
|
-
7. GRAFICI, SCALE E
|
|
269
|
+
7. GRAFICI, SCALE E STILE DINAMICO
|
|
247
270
|
========================================================================== */
|
|
248
271
|
.graph-wrapper {
|
|
249
272
|
position: relative;
|
|
@@ -252,7 +275,7 @@ body {
|
|
|
252
275
|
min-height: 0;
|
|
253
276
|
margin-top: 5px;
|
|
254
277
|
display: flex;
|
|
255
|
-
flex-direction: row;
|
|
278
|
+
flex-direction: row;
|
|
256
279
|
align-items: stretch;
|
|
257
280
|
background: rgba(0, 0, 0, 0.04);
|
|
258
281
|
border-radius: 4px;
|
|
@@ -265,15 +288,15 @@ body {
|
|
|
265
288
|
background: transparent !important;
|
|
266
289
|
}
|
|
267
290
|
|
|
268
|
-
/*
|
|
269
|
-
.
|
|
270
|
-
.
|
|
271
|
-
stroke-width: 1px !important;
|
|
291
|
+
/* I componenti interni dell'SVG gestiscono autonomamente spessori e colori via JS */
|
|
292
|
+
.graph-wrapper polygon,
|
|
293
|
+
.graph-wrapper line {
|
|
272
294
|
transition: all 0.3s ease;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
295
|
+
shape-rendering: geometricPrecision;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.graph-wrapper polygon {
|
|
299
|
+
filter: drop-shadow(0 0 2px rgba(0,0,0,0.05));
|
|
277
300
|
}
|
|
278
301
|
|
|
279
302
|
.scale-labels {
|
|
@@ -290,26 +313,12 @@ body {
|
|
|
290
313
|
}
|
|
291
314
|
|
|
292
315
|
/* --- COLONNA SINISTRA (STW, SOG) --- */
|
|
293
|
-
|
|
294
|
-
.box-stw .
|
|
295
|
-
order: 1;
|
|
296
|
-
}
|
|
297
|
-
.box-stw .scale-labels, .box-sog .scale-labels {
|
|
298
|
-
order: 2;
|
|
299
|
-
text-align: left;
|
|
300
|
-
padding-left: 4px;
|
|
301
|
-
}
|
|
316
|
+
.box-stw .sparkline, .box-sog .sparkline { order: 1; }
|
|
317
|
+
.box-stw .scale-labels, .box-sog .scale-labels { order: 2; text-align: left; padding-left: 4px; }
|
|
302
318
|
|
|
303
319
|
/* --- COLONNA DESTRA (DEPTH, TWS) --- */
|
|
304
|
-
|
|
305
|
-
.box-depth .
|
|
306
|
-
order: 1;
|
|
307
|
-
text-align: right;
|
|
308
|
-
padding-right: 4px;
|
|
309
|
-
}
|
|
310
|
-
.box-depth .sparkline, .box-tws .sparkline {
|
|
311
|
-
order: 2;
|
|
312
|
-
}
|
|
320
|
+
.box-depth .scale-labels, .box-tws .scale-labels { order: 1; text-align: right; padding-right: 4px; }
|
|
321
|
+
.box-depth .sparkline, .box-tws .sparkline { order: 2; }
|
|
313
322
|
|
|
314
323
|
/* --- REGOLE HERCULES (Zoom) --- */
|
|
315
324
|
.box-hercules { background: rgba(255, 0, 0, 0.05) !important; }
|
|
@@ -319,10 +328,8 @@ body {
|
|
|
319
328
|
letter-spacing: 1px; text-transform: uppercase; content: "HERCULES ";
|
|
320
329
|
}
|
|
321
330
|
|
|
322
|
-
/* Anche in modalità Hercules manteniamo 1px per coerenza, affidandoci al filtro per lo stacco visivo */
|
|
323
331
|
.line-hercules {
|
|
324
332
|
filter: drop-shadow(0 0 5px #ff0000);
|
|
325
|
-
stroke-width: 1px !important;
|
|
326
333
|
}
|
|
327
334
|
|
|
328
335
|
/* ==========================================================================
|
|
@@ -337,44 +344,24 @@ body {
|
|
|
337
344
|
|
|
338
345
|
.data-box { height: auto !important; }
|
|
339
346
|
|
|
340
|
-
|
|
341
|
-
.box-stw, .box-sog, .box-depth, .box-tws {
|
|
342
|
-
justify-content: flex-start !important;
|
|
343
|
-
/* Ridotto lo spazio superiore per avvicinare il valore al titolo */
|
|
344
|
-
padding-top: 1.2rem !important;
|
|
345
|
-
}
|
|
347
|
+
.box-stw, .box-sog, .box-depth, .box-tws { justify-content: flex-start !important; padding-top: 1.2rem !important; }
|
|
346
348
|
|
|
347
|
-
|
|
348
|
-
.box-stw .value,
|
|
349
|
-
.box-sog .value,
|
|
350
|
-
.box-depth .value,
|
|
351
|
-
.box-tws .value {
|
|
352
|
-
/* Aumentato a 30cqh per pareggiare il peso visivo di HEADING */
|
|
353
|
-
/* Alzato anche il limite cqw a 35 per permettere al numero di allargarsi */
|
|
349
|
+
.box-stw .value, .box-sog .value, .box-depth .value, .box-tws .value {
|
|
354
350
|
font-size: clamp(1.2rem, 30cqh, 35cqw) !important;
|
|
355
351
|
line-height: 0.8 !important;
|
|
356
|
-
margin-top: -4px !important;
|
|
352
|
+
margin-top: -4px !important;
|
|
357
353
|
margin-bottom: 4px !important;
|
|
358
354
|
padding-bottom: 0 !important;
|
|
359
|
-
font-weight: 700;
|
|
355
|
+
font-weight: 700;
|
|
360
356
|
}
|
|
361
357
|
|
|
362
|
-
|
|
363
|
-
.box-stw .graph-wrapper,
|
|
364
|
-
.box-sog .graph-wrapper,
|
|
365
|
-
.box-depth .graph-wrapper,
|
|
366
|
-
.box-tws .graph-wrapper {
|
|
358
|
+
.box-stw .graph-wrapper, .box-sog .graph-wrapper, .box-depth .graph-wrapper, .box-tws .graph-wrapper {
|
|
367
359
|
margin-top: 0 !important;
|
|
368
360
|
flex-grow: 1 !important;
|
|
369
361
|
}
|
|
370
362
|
|
|
371
|
-
|
|
372
|
-
.box-hdg, .box-cog, .box-twa, .box-awa, .box-twd, .box-tack {
|
|
373
|
-
justify-content: center !important;
|
|
374
|
-
padding-top: 1.2rem !important;
|
|
375
|
-
}
|
|
363
|
+
.box-hdg, .box-cog, .box-twa, .box-awa, .box-twd, .box-tack { justify-content: center !important; padding-top: 1.2rem !important; }
|
|
376
364
|
|
|
377
|
-
/* --- FOCUS MODE IN VERTICALE --- */
|
|
378
365
|
.main-container.focus-active {
|
|
379
366
|
grid-template-columns: 1fr !important;
|
|
380
367
|
grid-template-rows: 65vh 35vh !important;
|
|
@@ -384,7 +371,6 @@ body {
|
|
|
384
371
|
.focus-active .is-focused .value { font-size: clamp(4rem, 20cqh, 15rem) !important; margin-top: 20px !important; }
|
|
385
372
|
.focus-active .box-gauge { height: 35vh !important; width: 100vw !important; display: flex !important; align-items: center; justify-content: center; }
|
|
386
373
|
|
|
387
|
-
/* --- TACK IN VERTICALE --- */
|
|
388
374
|
.box-tack .dual-value-container { flex-direction: row !important; justify-content: space-between !important; align-items: center !important; }
|
|
389
375
|
.box-tack .dual-value-col { display: flex !important; flex-direction: column !important; justify-content: center; }
|
|
390
376
|
.box-tack .dual-value-col:first-child { align-items: flex-start !important; text-align: left !important; }
|
|
@@ -397,18 +383,11 @@ body {
|
|
|
397
383
|
========================================================================== */
|
|
398
384
|
#status {
|
|
399
385
|
position: absolute;
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
font-size: 0.6rem;
|
|
404
|
-
font-weight: 900;
|
|
405
|
-
text-transform: uppercase;
|
|
406
|
-
z-index: 1000;
|
|
407
|
-
/* Sfondo leggermente più marcato per leggerlo sopra il quadrante */
|
|
386
|
+
top: 15px; right: 15px;
|
|
387
|
+
font-size: 0.6rem; font-weight: 900;
|
|
388
|
+
text-transform: uppercase; z-index: 1000;
|
|
408
389
|
background: rgba(0,0,0,0.08);
|
|
409
|
-
padding: 3px 8px;
|
|
410
|
-
border-radius: 4px;
|
|
411
|
-
letter-spacing: 1px;
|
|
390
|
+
padding: 3px 8px; border-radius: 4px; letter-spacing: 1px;
|
|
412
391
|
}
|
|
413
392
|
.online { color: #27ae60; opacity: 0.8; }
|
|
414
393
|
.offline { color: #c0392b; font-weight: bold; }
|
|
@@ -435,11 +414,7 @@ rect[fill="#222"] { fill: #eee !important; }
|
|
|
435
414
|
|
|
436
415
|
/* ==========================================================================
|
|
437
416
|
10. NIGHT MODE (TACTICAL RED - ARCHITETTURA INTEGRALE)
|
|
438
|
-
==========================================================================
|
|
439
|
-
Questa sezione sovrascrive interamente lo schema Day Mode.
|
|
440
|
-
Obiettivo: Massima leggibilità, zero abbagliamento, preservazione visione notturna.
|
|
441
417
|
========================================================================== */
|
|
442
|
-
|
|
443
418
|
body.night-mode {
|
|
444
419
|
background-color: #000 !important;
|
|
445
420
|
color: #ff3333 !important;
|
|
@@ -448,130 +423,62 @@ body.night-mode {
|
|
|
448
423
|
/* --- 10.1 STRUTTURA DEI CONTENITORI --- */
|
|
449
424
|
.night-mode .data-box {
|
|
450
425
|
background: rgba(20, 0, 0, 0.4) !important;
|
|
451
|
-
border-color: #440000 !important;
|
|
426
|
+
border-color: #440000 !important;
|
|
452
427
|
box-shadow: inset 0 0 15px rgba(255, 0, 0, 0.05) !important;
|
|
453
428
|
}
|
|
454
429
|
|
|
455
430
|
/* --- 10.2 TIPOGRAFIA E TESTI --- */
|
|
456
|
-
|
|
457
|
-
.night-mode .
|
|
458
|
-
.night-mode .
|
|
459
|
-
.night-mode .
|
|
460
|
-
color: #800000 !important; /* Rosso sangue scuro per non distrarre */
|
|
461
|
-
}
|
|
431
|
+
.night-mode .label, .night-mode .unit, .night-mode .dual-label { color: #800000 !important; }
|
|
432
|
+
.night-mode .value-large { color: #ff3333 !important; text-shadow: 0 0 8px rgba(255, 0, 0, 0.4) !important; }
|
|
433
|
+
.night-mode .value { color: #ff3333; text-shadow: 0 0 8px rgba(255, 0, 0, 0.3); }
|
|
434
|
+
.night-mode #status { background: rgba(255, 0, 0, 0.1) !important; color: #ff3333 !important; }
|
|
462
435
|
|
|
463
|
-
/*
|
|
464
|
-
.night-mode .value-large {
|
|
465
|
-
color: #ff3333 !important;
|
|
466
|
-
text-shadow: 0 0 8px rgba(255, 0, 0, 0.4) !important;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/* Valori dinamici (SOG, TWS, STW):
|
|
470
|
-
Non usiamo !important per permettere al JavaScript di iniettare i colori dei Reef/Corrente */
|
|
471
|
-
.night-mode .value {
|
|
472
|
-
color: #ff3333;
|
|
473
|
-
text-shadow: 0 0 8px rgba(255, 0, 0, 0.3);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/* Widget di Stato */
|
|
477
|
-
.night-mode #status {
|
|
478
|
-
background: rgba(255, 0, 0, 0.1) !important;
|
|
479
|
-
color: #ff3333 !important;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/* --- 10.3 GRAFICI (STILE CHIRURGICO 1PX) --- */
|
|
436
|
+
/* --- 10.3 GRAFICI (STILE DINAMICO NIGHT MODE) --- */
|
|
483
437
|
.night-mode .graph-wrapper {
|
|
484
|
-
background: rgba(
|
|
438
|
+
background: rgba(10, 0, 0, 0.8) !important;
|
|
485
439
|
border: 1px solid #330000;
|
|
486
440
|
}
|
|
487
441
|
|
|
488
|
-
|
|
489
|
-
.night-mode .sparkline
|
|
490
|
-
|
|
442
|
+
.night-mode .sparkline line[stroke="rgba(0,0,0,0.12)"],
|
|
443
|
+
.night-mode .sparkline line[stroke="rgba(0,0,0,0.08)"] {
|
|
444
|
+
stroke: rgba(255, 0, 0, 0.1) !important; /* Griglia temporale rossa soffusa */
|
|
491
445
|
}
|
|
492
446
|
|
|
493
|
-
/*
|
|
494
|
-
.night-mode .
|
|
495
|
-
|
|
496
|
-
stroke: #ff3333 !important;
|
|
497
|
-
stroke-width: 1px !important;
|
|
498
|
-
filter: drop-shadow(0 0 2px rgba(255, 0, 0, 0.4));
|
|
447
|
+
/* Regolazione della luminosità notturna per preservare i gradienti e i colori del JS */
|
|
448
|
+
.night-mode .graph-wrapper svg {
|
|
449
|
+
filter: brightness(0.6) contrast(1.2);
|
|
499
450
|
}
|
|
500
451
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
452
|
+
.night-mode .graph-wrapper line[style] {
|
|
453
|
+
stroke-width: 1px !important;
|
|
454
|
+
filter: drop-shadow(0 0 2px rgba(255, 0, 0, 0.3));
|
|
504
455
|
}
|
|
505
456
|
|
|
506
|
-
/* LOGICA REEF TWS: Mantiene i colori degli allarmi sul grafico */
|
|
507
|
-
.night-mode .tws-reef-line { stroke-width: 1px !important; opacity: 0.9 !important; }
|
|
508
|
-
.night-mode .tws-reef-line[stroke="#000000"],
|
|
509
|
-
.night-mode .tws-reef-line[stroke="#000"] { stroke: #440000 !important; } /* Vento basso */
|
|
510
|
-
.night-mode .tws-reef-line[stroke="#e67e22"] { stroke: #b35500 !important; } /* 1° Reef */
|
|
511
|
-
.night-mode .tws-reef-line[stroke="#e74c3c"] {
|
|
512
|
-
stroke: #ff0000 !important;
|
|
513
|
-
filter: drop-shadow(0 0 2px #ff0000);
|
|
514
|
-
} /* 2° Reef */
|
|
515
|
-
|
|
516
|
-
/* Scale graduate dei grafici */
|
|
517
457
|
.night-mode .scale-labels { color: #800000 !important; }
|
|
518
458
|
.night-mode .is-focused .scale-labels { color: #ff3333 !important; }
|
|
519
459
|
|
|
520
460
|
/* --- 10.4 WIND GAUGE (STRUMENTO CENTRALE) --- */
|
|
521
|
-
|
|
522
|
-
.night-mode #wind-gauge circle {
|
|
523
|
-
fill: #000 !important;
|
|
524
|
-
stroke: #220000 !important;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
/* Cerchio interno e hotspot centrale (nero profondo) */
|
|
528
|
-
.night-mode #wind-gauge circle:nth-of-type(2),
|
|
529
|
-
.night-mode #fullscreen-hotspot {
|
|
530
|
-
fill: #050000 !important;
|
|
531
|
-
stroke: #330000 !important;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/* Effetto Glow e Logo Barca */
|
|
461
|
+
.night-mode #wind-gauge circle { fill: #000 !important; stroke: #220000 !important; }
|
|
462
|
+
.night-mode #wind-gauge circle:nth-of-type(2), .night-mode #fullscreen-hotspot { fill: #050000 !important; stroke: #330000 !important; }
|
|
535
463
|
.night-mode #center-glow feDropShadow { flood-color: #ff0000 !important; flood-opacity: 0.6 !important; }
|
|
536
|
-
|
|
537
|
-
.night-mode #boat-icon {
|
|
538
|
-
fill: #220000 !important; /* Rosso scurissimo per il corpo della barca */
|
|
539
|
-
stroke: #ff0000 !important; /* Bordo rosso vivo per definire la sagoma */
|
|
540
|
-
stroke-width: 0.8px !important; /* Spessore sottile ma netto */
|
|
541
|
-
opacity: 1 !important; /* Piena opacità per far risaltare il bordo */
|
|
542
|
-
}
|
|
543
|
-
/* Valori interni (AWS) */
|
|
464
|
+
.night-mode #boat-icon { fill: #220000 !important; stroke: #ff0000 !important; stroke-width: 0.8px !important; opacity: 1 !important; }
|
|
544
465
|
.night-mode #aws-display-group text { fill: #800000 !important; }
|
|
545
466
|
.night-mode #aws-val-svg { fill: #ff3333 !important; }
|
|
546
|
-
|
|
547
|
-
/* Tacche e Numeri Gradi della Bussola */
|
|
548
467
|
.night-mode #ticks line { stroke: #550000 !important; }
|
|
549
468
|
.night-mode #tick-labels { fill: #800000 !important; }
|
|
550
|
-
|
|
551
|
-
/* SETTORI VENTO: Conversione sicura per la notte */
|
|
552
469
|
.night-mode #wind-gauge path[stroke="#ff0000"] { stroke: #800000 !important; opacity: 0.8; }
|
|
553
|
-
.night-mode #wind-gauge path[stroke="#00ff00"] {
|
|
554
|
-
stroke: #440000 !important;
|
|
555
|
-
stroke-dasharray: 5, 4 !important;
|
|
556
|
-
opacity: 0.7;
|
|
557
|
-
} /* Verde Starboard -> Rosso tratteggiato */
|
|
470
|
+
.night-mode #wind-gauge path[stroke="#00ff00"] { stroke: #440000 !important; stroke-dasharray: 5, 4 !important; opacity: 0.7; }
|
|
558
471
|
.night-mode #wind-gauge path[stroke="#ff8800"] { stroke: #330000 !important; opacity: 0.6; }
|
|
559
|
-
|
|
560
|
-
/* Lancette AWA/TWA e COG */
|
|
561
472
|
.night-mode #awa-pointer path { fill: #ff0000 !important; stroke: #000 !important; }
|
|
562
473
|
.night-mode #twa-pointer path { fill: #800000 !important; stroke: #000 !important; }
|
|
563
474
|
.night-mode #track-pointer path { fill: #ff3333 !important; stroke: none !important; }
|
|
564
475
|
|
|
565
476
|
/* --- 10.5 WIDGETS ACCESSORI --- */
|
|
566
|
-
/* Barra Leeway */
|
|
567
477
|
.night-mode #leeway-val { fill: #ff3333 !important; color: #ff3333 !important; }
|
|
568
478
|
.night-mode rect[fill="url(#leeway-grad)"] { fill: url(#leeway-night-grad) !important; }
|
|
569
|
-
.night-mode rect[fill="#eee"],
|
|
570
|
-
.night-mode rect[fill="#222"] { fill: #0a0000 !important; stroke: #330000 !important; }
|
|
479
|
+
.night-mode rect[fill="#eee"], .night-mode rect[fill="#222"] { fill: #0a0000 !important; stroke: #330000 !important; }
|
|
571
480
|
.night-mode g[stroke="#555"] line { stroke: #440000 !important; }
|
|
572
481
|
.night-mode g[fill="#555"] text { fill: #800000 !important; }
|
|
573
|
-
|
|
574
|
-
/* Mini Bussola TWD */
|
|
575
482
|
.night-mode .mini-compass { border-color: #330000 !important; background: #000 !important; }
|
|
576
483
|
.night-mode .mini-compass text { fill: #800000 !important; }
|
|
577
484
|
.night-mode .mini-compass text:last-of-type { fill: #800000 !important; opacity: 1; }
|
|
@@ -579,10 +486,9 @@ body.night-mode {
|
|
|
579
486
|
.night-mode #twd-arrow #twd-wind-chevron { stroke: #ff3333 !important; }
|
|
580
487
|
|
|
581
488
|
/* --- 10.6 LOGICA RED-SHIFT TREND --- */
|
|
582
|
-
|
|
583
|
-
.night-mode circle[fill="#
|
|
584
|
-
.night-mode circle[fill="#
|
|
585
|
-
.night-mode circle[fill="#c0392b"] { fill: #800000 !important; } /* Header */
|
|
489
|
+
.night-mode circle[fill="#bbb"], .night-mode circle[fill="#bbbbbb"] { fill: #440000 !important; }
|
|
490
|
+
.night-mode circle[fill="#27ae60"] { fill: #ff0000 !important; }
|
|
491
|
+
.night-mode circle[fill="#c0392b"] { fill: #800000 !important; }
|
|
586
492
|
|
|
587
493
|
/* ==========================================================================
|
|
588
494
|
11. TREND E ANIMAZIONI
|
|
@@ -592,3 +498,20 @@ body.night-mode {
|
|
|
592
498
|
.is-trending { opacity: 1 !important; animation: blink-trend 1s infinite !important; }
|
|
593
499
|
.is-gybing { opacity: 1 !important; animation: none !important; filter: drop-shadow(0 0 8px #ff0000) !important; }
|
|
594
500
|
.unstable-data { animation: blink-trend 1.5s infinite ease-in-out; color: #e67e22 !important; }
|
|
501
|
+
|
|
502
|
+
/* --- CORREZIONE VISIBILITÀ COLORI BASE SCURI IN NIGHT MODE --- */
|
|
503
|
+
/* Il Navy Slate (#2c3e50) diventa nero con il filtro notturno. Lo schiariamo a un Grigio-Azzurro. */
|
|
504
|
+
.night-mode .graph-wrapper line[style*="#2c3e50"] {
|
|
505
|
+
stroke: #6c8ea0 !important;
|
|
506
|
+
}
|
|
507
|
+
.night-mode .graph-wrapper polygon[style*="#2c3e50"] {
|
|
508
|
+
fill: #6c8ea0 !important;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/* Opzionale: Diamo una piccola "spinta" anche all'azzurro della Depth per bilanciarlo */
|
|
512
|
+
.night-mode .graph-wrapper line[style*="#0088cc"] {
|
|
513
|
+
stroke: #33aadd !important;
|
|
514
|
+
}
|
|
515
|
+
.night-mode .graph-wrapper polygon[style*="#0088cc"] {
|
|
516
|
+
fill: #33aadd !important;
|
|
517
|
+
}
|