@sailingrotevista/rotevista-dash 7.0.7 → 7.0.8

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.
Files changed (2) hide show
  1. package/debug.html +262 -71
  2. package/package.json +1 -1
package/debug.html CHANGED
@@ -1,9 +1,9 @@
1
1
  <!DOCTYPE html>
2
- <html lang="it">
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>SignalK Data Debugger & Logger CSV (Pro v6.0)</title>
6
+ <title>SignalK Data Debugger & CSV Dumper (Pro v6.0)</title>
7
7
  <style>
8
8
  body {
9
9
  background-color: #121212;
@@ -40,12 +40,12 @@
40
40
 
41
41
  /* GRIGLIA WORKSPACE: AFFIANCA SELEZIONE E TABELLA VALORI */
42
42
  .workspace-grid {
43
- display: grid;
44
- grid-template-columns: 1fr 1.3fr;
45
- gap: 15px;
43
+ display: flex;
44
+ flex-direction: row;
46
45
  flex-shrink: 0;
47
46
  margin-bottom: 15px;
48
47
  min-height: 0;
48
+ height: 380px; /* Altezza fissa dei pannelli superiori */
49
49
  }
50
50
 
51
51
  /* COLONNA SINISTRA: Pannello dei filtri ad albero */
@@ -55,9 +55,13 @@
55
55
  border-radius: 8px;
56
56
  padding: 15px;
57
57
  box-sizing: border-box;
58
- height: 380px; /* Altezza fissa allineata alla tabella di destra */
58
+ width: 45%; /* Larghezza iniziale di default */
59
+ min-width: 200px;
60
+ max-width: 80%;
59
61
  display: flex;
60
62
  flex-direction: column;
63
+ overflow: hidden; /* Forza lo scorrimento dei figli all'interno dell'altezza fissa */
64
+ flex: none; /* Impedisce a Flexbox di sovrascrivere la larghezza impostata via JS durante il drag */
61
65
  }
62
66
  .presets {
63
67
  margin: 10px 0;
@@ -67,6 +71,53 @@
67
71
  flex-shrink: 0;
68
72
  }
69
73
 
74
+ /* INPUT BARRA DI RICERCA VELOCE */
75
+ .search-input {
76
+ width: 100%;
77
+ padding: 8px 12px;
78
+ background: #111;
79
+ color: #fff;
80
+ border: 1px solid #333;
81
+ border-radius: 6px;
82
+ box-sizing: border-box;
83
+ font-family: monospace;
84
+ font-size: 11px;
85
+ margin-top: 10px;
86
+ outline: none;
87
+ transition: border-color 0.2s;
88
+ flex-shrink: 0;
89
+ }
90
+ .search-input:focus {
91
+ border-color: #4CAF50;
92
+ }
93
+
94
+ /* DIVISIONE ORIZZONTALE RIDIMENSIONABILE A PIACIMENTO (SPLITTER DRAG BAR) */
95
+ .splitter {
96
+ width: 6px;
97
+ background: #222;
98
+ cursor: col-resize;
99
+ flex-shrink: 0;
100
+ transition: background 0.15s ease;
101
+ border-radius: 3px;
102
+ margin: 0 5px;
103
+ touch-action: none; /* Impedisce lo scorrimento della pagina web su tablet durante il drag */
104
+ }
105
+ .splitter:hover, .splitter.dragging {
106
+ background: #4CAF50;
107
+ }
108
+
109
+ /* COLONNA DESTRA: Pannello Tabella Valori Attuali */
110
+ .summary-panel {
111
+ background: #1a1a1a;
112
+ border: 1px solid #333;
113
+ border-radius: 8px;
114
+ padding: 15px;
115
+ box-sizing: border-box;
116
+ flex-grow: 1; /* Occupa tutto lo spazio a destra dello splitter */
117
+ min-width: 200px;
118
+ overflow-y: auto;
119
+ }
120
+
70
121
  /* STRUTTURA AD ALBERO NIDIFICATO INTERNO */
71
122
  .trees-container {
72
123
  margin-top: 15px;
@@ -74,7 +125,7 @@
74
125
  padding-top: 15px;
75
126
  flex-grow: 1; /* Occupa tutto lo spazio interno rimasto nel contenitore fisso */
76
127
  overflow-y: auto;
77
- display: block;
128
+ display: block; /* Cambiato a block per garantire il ricalcolo dello scroll interno */
78
129
  }
79
130
  .tree-node {
80
131
  display: flex;
@@ -126,16 +177,6 @@
126
177
  padding-left: 5px;
127
178
  }
128
179
 
129
- /* COLONNA DESTRA: Pannello Tabella Valori Attuali */
130
- .summary-panel {
131
- background: #1a1a1a;
132
- border: 1px solid #333;
133
- border-radius: 8px;
134
- padding: 15px;
135
- box-sizing: border-box;
136
- height: 380px; /* Altezza fissa allineata alla selezione di sinistra */
137
- overflow-y: auto;
138
- }
139
180
  table {
140
181
  width: 100%;
141
182
  border-collapse: collapse;
@@ -211,43 +252,50 @@
211
252
  </head>
212
253
  <body>
213
254
 
214
- <h1>SignalK Data Debugger & Logger CSV (Pro v6.0)</h1>
255
+ <h1>SignalK Data Debugger & CSV Dumper (Pro v6.0)</h1>
215
256
 
216
257
  <div class="controls">
217
258
  <label>Server IP:</label>
218
- <input type="text" id="server-ip" value="192.168.111.240:3000">
219
- <button id="btn-scan" onclick="startScan()">Scansiona Rete (10s)</button>
220
- <button id="btn-listen" onclick="toggleListening()" class="secondary" disabled>Ascolta</button>
221
- <span id="status">DISCONNESSO</span>
259
+ <input type="text" id="server-ip" value="venus.local:3000">
260
+ <button id="btn-scan" onclick="startScan()">Scan Network (10s)</button>
261
+ <button id="btn-listen" onclick="toggleListening()" class="secondary" disabled>Listen</button>
262
+ <span id="status">DISCONNECTED</span>
222
263
  </div>
223
264
 
224
- <!-- CONTENITORE PRINCIPALE DI LAVORO A DUE COLONNE -->
225
- <div class="workspace-grid">
265
+ <!-- CONTENITORE PRINCIPALE DI LAVORO CON STRUTTURA FLUIDA ED ELEMENTI INTERATTIVI -->
266
+ <div class="workspace-grid" id="grid-container">
226
267
 
227
268
  <!-- COLONNA SINISTRA: AREA FILTRI AVANZATA CON ALBERO GERARCHICO -->
228
- <div class="filter-container">
229
- <span style="font-weight: bold; color: #4CAF50; font-size: 13px;">Seleziona percorsi da scrivere nel Log CSV (Albero Gerarchico Completo):</span>
269
+ <div class="filter-container" id="pane-left">
270
+ <span style="font-weight: bold; color: #4CAF50; font-size: 13px;">Select paths to write to CSV Dump (Complete Hierarchical Tree):</span>
230
271
  <div class="presets">
231
- <button class="secondary" onclick="applyPreset('all')">Seleziona Tutto</button>
232
- <button class="secondary" style="background-color: #2c3e50;" onclick="applyPreset('twd')">Analisi Bussola Meteo (TWD/Vento/Rotta)</button>
233
- <button class="secondary" style="background-color: #0088cc;" onclick="applyPreset('gps')">Solo GPS / Rotte</button>
234
- <button class="secondary" onclick="applyPreset('none')">Deseleziona Tutto</button>
272
+ <button class="secondary" onclick="applyPreset('all')">Select All</button>
273
+ <button class="secondary" style="background-color: #2c3e50;" onclick="applyPreset('twd')">Weather Compass Analysis (TWD/Wind/Course)</button>
274
+ <button class="secondary" style="background-color: #0088cc;" onclick="applyPreset('gps')">GPS / Course Only</button>
275
+ <button class="secondary" onclick="applyPreset('none')">Deselect All</button>
235
276
  </div>
277
+
278
+ <!-- INPUT BARRA DI RICERCA VELOCE -->
279
+ <input type="text" id="tree-search" class="search-input" placeholder="Search path... (e.g. solar, temp, gps, batteries)" oninput="filterTree(this.value)">
280
+
236
281
  <div id="path-trees-container" class="trees-container">
237
282
  <!-- Categorie ed alberi gerarchici autogenerati dinamicamente via JS al termine della scansione -->
238
283
  </div>
239
284
  </div>
240
285
 
286
+ <!-- DIVISIONE ORIZZONTALE RIDIMENSIONABILE A PIACIMENTO (SPLITTER DRAG BAR) -->
287
+ <div class="splitter" id="drag-splitter"></div>
288
+
241
289
  <!-- COLONNA DESTRA: PANNELLO TABELLA RIASSUNTIVA VALORI IN TEMPO REALE -->
242
- <div class="summary-panel">
290
+ <div class="summary-panel" id="pane-right">
243
291
  <table>
244
292
  <thead>
245
293
  <tr>
246
294
  <!-- BLOCCO RIGIDO DELLE LARGHEZZE DELLE COLONNE -->
247
- <th width="30%">Path (Percorso)</th>
248
- <th width="33%">Valore Convertito</th>
249
- <th width="22%">Sorgente (Sensore - Dettagliato)</th>
250
- <th width="15%">Ultimo Ricevuto</th>
295
+ <th width="30%">Path</th>
296
+ <th width="33%">Converted Value</th>
297
+ <th width="22%">Source (Detailed Sensor)</th>
298
+ <th width="15%">Last Received</th>
251
299
  </tr>
252
300
  </thead>
253
301
  <tbody id="data-table">
@@ -261,11 +309,11 @@
261
309
  <!-- PANNELLO LOG SEQUENZIALE (CSV) - POSIZIONATO A TUTTA LARGHEZZA SUL FONDO -->
262
310
  <div class="log-panel">
263
311
  <div class="log-header">
264
- <span>Event Log CSV (Timestamp, Path, Value, Unit, Source, Spike)</span>
312
+ <span>CSV Event Dump (Timestamp, Path, Value, Unit, Source, Spike)</span>
265
313
  <div style="display: flex; align-items: center;">
266
- <span id="copy-notification">Copiato!</span>
267
- <button class="action" onclick="copyLogCSV()" style="padding: 4px 10px; font-size: 11px; margin-right: 10px;">Copia CSV</button>
268
- <button class="secondary" onclick="clearLog()" style="padding: 4px 10px; font-size: 11px;">Pulisci</button>
314
+ <span id="copy-notification">Copied!</span>
315
+ <button class="action" onclick="copyLogCSV()" style="padding: 4px 10px; font-size: 11px; margin-right: 10px;">Copy Dump</button>
316
+ <button class="secondary" onclick="clearLog()" style="padding: 4px 10px; font-size: 11px;">Clear</button>
269
317
  </div>
270
318
  </div>
271
319
 
@@ -348,7 +396,7 @@
348
396
  if (hasChildren) {
349
397
  const toggleSpan = document.createElement('span');
350
398
  toggleSpan.className = 'node-toggle';
351
- toggleSpan.innerText = '[-]';
399
+ toggleSpan.innerText = '[+]'; // Compresso di default all'avvio
352
400
  toggleSpan.onclick = (e) => {
353
401
  e.stopPropagation();
354
402
  const childContainer = nodeDiv.querySelector(':scope > .node-children');
@@ -370,7 +418,7 @@
370
418
  // 2. Checkbox di selezione
371
419
  const chk = document.createElement('input');
372
420
  chk.type = 'checkbox';
373
- chk.checked = true;
421
+ chk.checked = false; // Deselezionato di default per focalizzare lo spazio
374
422
  chk.className = 'node-checkbox';
375
423
 
376
424
  if (!hasChildren) {
@@ -393,10 +441,11 @@
393
441
 
394
442
  nodeDiv.appendChild(rowDiv);
395
443
 
396
- // 4. Creazione ricorsiva dei nodi figli (Sotto-Albero)
444
+ // 4. Creazione ricorsiva dei nodi figli (Sotto-Albero nascosto di default all'avvio)
397
445
  if (hasChildren) {
398
446
  const childrenDiv = document.createElement('div');
399
447
  childrenDiv.className = 'node-children';
448
+ childrenDiv.style.display = 'none'; // Chiuso di default all'avvio
400
449
  renderTreeDOM(item._children, childrenDiv, false);
401
450
  nodeDiv.appendChild(childrenDiv);
402
451
  }
@@ -424,21 +473,103 @@
424
473
 
425
474
  // 3. FILTRAGGIO DINAMICO DELLA TABELLA: Aggiorna istantaneamente la visibilità dei valori
426
475
  updateTableVisibility();
476
+
477
+ // 4. FILTRAGGIO DINAMICO DEL DUMP CSV: Aggiorna lo schermo dei log al cambio di selezione
478
+ renderLogTextArea();
427
479
  });
428
480
 
429
- // Aggiorna istantaneamente la visibilità dei valori della tabella in base alle spunte
481
+ // Filtra l'albero gerarchico visivo in tempo reale con espansione automatica dei rami corrispondenti
482
+ function filterTree(query) {
483
+ const q = query.toLowerCase().trim();
484
+ const leaves = document.querySelectorAll('.tree-leaf');
485
+ const branches = Array.from(document.querySelectorAll('.tree-branch')).reverse(); // Scansione invertita per propagare lo stato ai nonni
486
+
487
+ if (q === "") {
488
+ // Ripristina la visibilità totale e richiude i rami se la query viene cancellata
489
+ leaves.forEach(leaf => {
490
+ leaf.style.display = '';
491
+ });
492
+ branches.forEach(branch => {
493
+ branch.style.display = '';
494
+ const childContainer = branch.querySelector(':scope > .node-children');
495
+ const toggleSpan = branch.querySelector(':scope > .node-row > .node-toggle');
496
+ if (childContainer && toggleSpan) {
497
+ childContainer.style.display = 'none';
498
+ toggleSpan.innerText = '[+]';
499
+ }
500
+ });
501
+ updateTableVisibility();
502
+ renderLogTextArea();
503
+ return;
504
+ }
505
+
506
+ // 1. Filtra tutte le foglie (Parametri finali) e AUTO-SELEZIONA quelle corrispondenti
507
+ leaves.forEach(leaf => {
508
+ const labelEl = leaf.querySelector('.node-label');
509
+ const chk = leaf.querySelector('input');
510
+ if (!labelEl || !chk) return;
511
+
512
+ const label = labelEl.innerText.toLowerCase();
513
+ const path = (chk.dataset.path || "").toLowerCase();
514
+
515
+ if (label.includes(q) || path.includes(q)) {
516
+ leaf.style.display = ''; // Corrispondenza trovata
517
+ chk.checked = true; // AUTO-SELEZIONE dei parametri corrispondenti!
518
+ } else {
519
+ leaf.style.display = 'none'; // Nasconde se non corrisponde
520
+ }
521
+ });
522
+
523
+ // 2. Filtra e auto-espande i rami genitori dal basso verso l'alto
524
+ branches.forEach(branch => {
525
+ const childContainer = branch.querySelector(':scope > .node-children');
526
+ const toggleSpan = branch.querySelector(':scope > .node-row > .node-toggle');
527
+ if (!childContainer) return;
528
+
529
+ // Conta quanti sotto-nodi (sia rami che foglie) sono rimasti visibili
530
+ const visibleChildren = childContainer.querySelectorAll('.tree-node:not([style*="display: none"])');
531
+
532
+ if (visibleChildren.length > 0) {
533
+ branch.style.display = ''; // Mostra il ramo genitore poiché contiene elementi corrispondenti
534
+ if (toggleSpan) {
535
+ childContainer.style.display = 'flex'; // Auto-espande la cartella per mostrare il match
536
+ toggleSpan.innerText = '[-]';
537
+ }
538
+ } else {
539
+ branch.style.display = 'none'; // Nasconde l'intera cartella se nessun figlio corrisponde alla ricerca
540
+ }
541
+ });
542
+
543
+ // 3. Ricalcola gli stati indeterminati dei genitori e aggiorna la tabella in tempo reale
544
+ updateAllTreeMasterCheckboxes();
545
+ updateTableVisibility();
546
+ renderLogTextArea(); // Filtra il Dump CSV sullo schermo ad ogni input della ricerca!
547
+ }
548
+
549
+ // Aggiorna istantaneamente la visibilità dei valori della tabella in base alle spunte e alla barra di ricerca
430
550
  function updateTableVisibility() {
551
+ const queryInput = document.getElementById('tree-search');
552
+ const q = queryInput ? queryInput.value.toLowerCase().trim() : "";
553
+
431
554
  registeredPaths.forEach(path => {
432
555
  const safeId = path.replace(/\./g, '-');
433
556
  const chk = document.getElementById(`chk-${safeId}`);
434
557
  const row = document.getElementById(`row-${safeId}`);
435
558
 
436
559
  if (row) {
437
- // Durante la scansione mostriamo tutto. Fuori scansione mostriamo solo se la spunta corrispondente è attiva.
438
- if (isScanning || !chk || chk.checked) {
439
- row.style.display = ''; // Mostra (table-row)
560
+ if (isScanning) {
561
+ // Durante la scansione mostriamo tutte le righe scoperte in tempo reale
562
+ row.style.display = '';
440
563
  } else {
441
- row.style.display = 'none'; // Nasconde
564
+ // Fuori scansione la riga è visibile solo se è spuntata E se il percorso corrisponde alla ricerca attiva (se presente)
565
+ const matchesQuery = q === "" || path.toLowerCase().includes(q);
566
+ const isChecked = chk && chk.checked;
567
+
568
+ if (isChecked && matchesQuery) {
569
+ row.style.display = ''; // Mostra
570
+ } else {
571
+ row.style.display = 'none'; // Nasconde
572
+ }
442
573
  }
443
574
  }
444
575
  });
@@ -483,6 +614,7 @@
483
614
  });
484
615
  updateAllTreeMasterCheckboxes();
485
616
  updateTableVisibility(); // Sincronizza la tabella visiva al cambio di preset
617
+ renderLogTextArea(); // Filtra dinamicamente anche i log CSV in base al preset!
486
618
  }
487
619
 
488
620
  // =========================================================
@@ -515,6 +647,7 @@
515
647
  pathTree = {};
516
648
  document.getElementById('path-trees-container').innerHTML = '';
517
649
  document.getElementById('data-table').innerHTML = '';
650
+ document.getElementById('tree-search').value = ''; // Resetta il testo della barra di ricerca
518
651
 
519
652
  const btnScan = document.getElementById('btn-scan');
520
653
  const btnListen = document.getElementById('btn-listen');
@@ -526,12 +659,12 @@
526
659
  scanCountdown = 10;
527
660
 
528
661
  btnScan.disabled = true;
529
- btnScan.innerText = `Scansione... ${scanCountdown}s`;
662
+ btnScan.innerText = `Scanning... ${scanCountdown}s`;
530
663
  btnListen.disabled = true;
531
664
  btnListen.className = "secondary";
532
- btnListen.innerText = "Ascolta";
665
+ btnListen.innerText = "Listen";
533
666
 
534
- status.innerText = "SCANSIONE IN CORSO";
667
+ status.innerText = "SCAN IN PROGRESS";
535
668
  status.className = "scanning";
536
669
 
537
670
  socket = new WebSocket(`ws://${ip}/signalk/v1/stream?subscribe=self`);
@@ -543,26 +676,27 @@
543
676
  scanTimer = setInterval(() => {
544
677
  scanCountdown--;
545
678
  if (scanCountdown > 0) {
546
- btnScan.innerText = `Scansione... ${scanCountdown}s`;
679
+ btnScan.innerText = `Scanning... ${scanCountdown}s`;
547
680
  } else {
548
681
  // Scansione terminata! Generazione ricorsiva dell'albero gerarchico reale
549
682
  clearInterval(scanTimer);
550
683
  scanTimer = null;
551
- socket.close();
684
+
685
+ isScanning = false; // 1. Imposta lo stato a false PRIMA di chiudere la connessione
686
+ socket.close(); // 2. Scatena l'evento onclose (che ora ignorerà l'interruzione di scansione)
552
687
 
553
688
  const treeContainer = document.getElementById('path-trees-container');
554
689
  renderTreeDOM(pathTree, treeContainer, true); // Genera ricorsivamente l'albero
555
690
  updateAllTreeMasterCheckboxes(); // Allinea gli stati dei padri
556
- updateTableVisibility(); // Inizializza i filtri della tabella
691
+ updateTableVisibility(); // Inizializza i filtri della tabella (Nasconde tutto all'avvio)
557
692
 
558
- isScanning = false;
559
693
  btnScan.disabled = false;
560
- btnScan.innerText = "Scansiona Rete (10s)";
694
+ btnScan.innerText = "Scan Network (10s)";
561
695
 
562
696
  btnListen.disabled = false;
563
697
  btnListen.className = "";
564
698
 
565
- status.innerText = "SCANSIONE COMPLETATA";
699
+ status.innerText = "SCAN COMPLETED";
566
700
  status.className = "online";
567
701
  addLogEntry(new Date().toISOString(), "SYSTEM", "Scan Phase Finished", "-", "-", false);
568
702
  }
@@ -596,8 +730,8 @@
596
730
  clearInterval(scanTimer);
597
731
  isScanning = false;
598
732
  btnScan.disabled = false;
599
- btnScan.innerText = "Scansiona Rete (10s)";
600
- status.innerText = "SCANSIONE FALLITA";
733
+ btnScan.innerText = "Scan Network (10s)";
734
+ status.innerText = "SCAN FAILED";
601
735
  status.className = "";
602
736
  }
603
737
  };
@@ -615,10 +749,10 @@
615
749
  if (isListening) {
616
750
  if (socket) socket.close();
617
751
  isListening = false;
618
- btnListen.innerText = "Ascolta";
752
+ btnListen.innerText = "Listen";
619
753
  btnListen.className = "";
620
754
  btnScan.disabled = false;
621
- status.innerText = "ASCOLTO INTERROTTO";
755
+ status.innerText = "LISTENING STOPPED";
622
756
  status.className = "online";
623
757
  addLogEntry(new Date().toISOString(), "SYSTEM", "Listening Stopped", "-", "-", false);
624
758
  return;
@@ -626,10 +760,10 @@
626
760
 
627
761
  isListening = true;
628
762
  btnScan.disabled = true;
629
- btnListen.innerText = "Stop Ascolto";
763
+ btnListen.innerText = "Stop Dump";
630
764
  btnListen.className = "stop";
631
765
 
632
- status.innerText = "IN ASCOLTO SELETTIVO...";
766
+ status.innerText = "DUMPING VALUES...";
633
767
  status.className = "online";
634
768
 
635
769
  lastValues = { "navigation.speedOverGround": 0, "navigation.courseOverGroundTrue": 0 };
@@ -654,11 +788,20 @@
654
788
  const safeId = v.path.replace(/\./g, '-');
655
789
  const chk = document.getElementById(`chk-${safeId}`);
656
790
 
791
+ // Verifica sia la spunta sia l'effettiva visibilità a schermo (non nascosto da ricerca!)
792
+ let isLogged = false;
657
793
  if (chk && chk.checked) {
658
- // Scrive e logga nel CSV solo se la checkbox corrispondente è spuntata!
794
+ const leaf = chk.closest('.tree-leaf');
795
+ if (leaf && leaf.style.display !== 'none') {
796
+ isLogged = true;
797
+ }
798
+ }
799
+
800
+ if (isLogged) {
801
+ // Scrive e logga nel CSV solo se la checkbox corrispondente è spuntata e visibile!
659
802
  processData(v.path, v.value, sourceName, timeISO, timeUI, true);
660
803
  } else {
661
- // Se non è spuntata, aggiorna passivamente la tabella silenziando i log
804
+ // Se non è spuntata o è nascosta, aggiorna passivamente la tabella silenziando i log
662
805
  if (registeredPaths.has(v.path)) {
663
806
  processData(v.path, v.value, sourceName, timeISO, timeUI, false);
664
807
  }
@@ -675,7 +818,7 @@
675
818
  socket.onclose = () => {
676
819
  if (isListening) {
677
820
  isListening = false;
678
- btnListen.innerText = "Ascolta";
821
+ btnListen.innerText = "Listen";
679
822
  btnListen.className = "";
680
823
  btnScan.disabled = false;
681
824
  status.innerText = "DISCONNESSO";
@@ -685,7 +828,7 @@
685
828
  };
686
829
 
687
830
  socket.onerror = () => {
688
- status.innerText = "ERRORE CONNESSIONE";
831
+ status.innerText = "CONNECTION ERROR";
689
832
  addLogEntry(new Date().toISOString(), "SYSTEM", "Connection Error", "-", "-", true);
690
833
  };
691
834
  }
@@ -803,19 +946,36 @@
803
946
  renderLogTextArea();
804
947
  }
805
948
 
949
+ // Filtra ricorsivamente i log visualizzati nel Dump CSV in tempo reale in base alla selezione attiva e visibile
806
950
  function renderLogTextArea() {
807
951
  const textArea = document.getElementById('log-container');
808
- textArea.value = csvHeader + "\n" + logLinesArray.join("\n");
952
+ if (!textArea) return;
953
+
954
+ const filteredLines = logLinesArray.filter(line => {
955
+ const parts = line.split(',');
956
+ const path = parts[1]; // L'indice 1 corrisponde al percorso dati del pacchetto
957
+ if (!path || path === "Path" || path === "SYSTEM") return true; // Preserva le intestazioni e i messaggi di connessione di sistema
958
+
959
+ const safeId = path.replace(/\./g, '-');
960
+ const chk = document.getElementById(`chk-${safeId}`);
961
+
962
+ // Filtro integrato: la linea viene visualizzata solo se spuntata E visibile (non esclusa dalla ricerca)
963
+ if (chk && chk.checked) {
964
+ const leaf = chk.closest('.tree-leaf');
965
+ return leaf && leaf.style.display !== 'none';
966
+ }
967
+ return false;
968
+ });
969
+
970
+ textArea.value = csvHeader + "\n" + filteredLines.join("\n");
809
971
  textArea.scrollTop = textArea.scrollHeight;
810
972
  }
811
973
 
812
- // Pulisce l'area di testo dei log sequenziali
813
974
  function clearLog() {
814
975
  logLinesArray = [];
815
976
  renderLogTextArea();
816
977
  }
817
978
 
818
- // Copia negli appunti il testo del log CSV
819
979
  function copyLogCSV() {
820
980
  const textArea = document.getElementById('log-container');
821
981
  textArea.select();
@@ -830,17 +990,48 @@
830
990
  }
831
991
  } catch (err) {
832
992
  console.error("Copia fallita: ", err);
833
- alert("Errore nella copia degli appunti. Usa CTRL+C / CMD+C manualmente.");
993
+ alert("Error copying to clipboard. Please use CTRL+C / CMD+C manually.");
834
994
  }
835
995
  }
836
996
 
837
- // Mostra e sfuma la notifica di copia riuscita
838
997
  function showCopyNotif() {
839
998
  const notif = document.getElementById('copy-notification');
840
999
  notif.style.opacity = 1;
841
1000
  setTimeout(() => notif.style.opacity = 0, 2000);
842
1001
  window.getSelection().removeAllRanges();
843
1002
  }
1003
+
1004
+ // =========================================================
1005
+ // LOGICA DI GESTIONE DELLO SPLITTER ORIZZONTALE (DRAG BAR)
1006
+ // =========================================================
1007
+ const splitter = document.getElementById('drag-splitter');
1008
+ const leftPane = document.getElementById('pane-left');
1009
+ const gridContainer = document.getElementById('grid-container');
1010
+
1011
+ splitter.addEventListener('pointerdown', (e) => {
1012
+ e.preventDefault();
1013
+ splitter.classList.add('dragging');
1014
+
1015
+ const startX = e.clientX;
1016
+ const startWidth = leftPane.getBoundingClientRect().width;
1017
+ const containerWidth = gridContainer.getBoundingClientRect().width;
1018
+
1019
+ const onPointerMove = (moveEvent) => {
1020
+ const deltaX = moveEvent.clientX - startX;
1021
+ // Limita il ridimensionamento tra 200px e l'80% dello schermo per sicurezza
1022
+ const newWidth = Math.max(200, Math.min(containerWidth * 0.8, startWidth + deltaX));
1023
+ leftPane.style.width = `${(newWidth / containerWidth) * 100}%`;
1024
+ };
1025
+
1026
+ const onPointerUp = () => {
1027
+ splitter.classList.remove('dragging');
1028
+ document.removeEventListener('pointermove', onPointerMove);
1029
+ document.removeEventListener('pointerup', onPointerUp);
1030
+ };
1031
+
1032
+ document.addEventListener('pointermove', onPointerMove);
1033
+ document.addEventListener('pointerup', onPointerUp);
1034
+ });
844
1035
  </script>
845
1036
  </body>
846
1037
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "7.0.7",
3
+ "version": "7.0.8",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {