Se mejora la interfaz

This commit is contained in:
Exequiel Aramburu 2026-09-30 00:20:44 -03:00
parent 5fc3437ce2
commit 3c67394d1f
13 changed files with 404 additions and 170 deletions

View File

@ -74,7 +74,7 @@ if (actualizar) {
frame: false, frame: false,
show: false, // Oculta la ventana al crearla show: false, // Oculta la ventana al crearla
width: 350, width: 350,
height: 200 height: 203
}, function(win) { }, function(win) {
gui.Screen.Init(); gui.Screen.Init();
setTimeout(function() { setTimeout(function() {

View File

@ -12,6 +12,7 @@
<link rel="stylesheet" href="../node_modules/datatables.net-buttons-bs5/css/buttons.bootstrap5.min.css"> <link rel="stylesheet" href="../node_modules/datatables.net-buttons-bs5/css/buttons.bootstrap5.min.css">
<link rel="stylesheet" href="../node_modules/nprogress/nprogress.css"> <link rel="stylesheet" href="../node_modules/nprogress/nprogress.css">
<link rel="stylesheet" href="../gui_common/css/style.css"> <link rel="stylesheet" href="../gui_common/css/style.css">
<link rel="stylesheet" href="../gui_common/css/visual.css">
<!-- Librerías JS --> <!-- Librerías JS -->
@ -60,7 +61,7 @@
<div id="cargador" class="d-flex justify-content-center align-items-center" style="background-color: #042507;"> <div id="cargador" class="d-flex justify-content-center align-items-center" style="background-color: #042507;">
<div class="progressinicio"> <div class="progressinicio">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="background-color: #30511b; width: 100%"> <div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="background-color: #30511b; width: 100%">
<img id="loadinicio" class="mx-auto d-block" src="../gui_common/iconos/icono_app.webp" height="120px" width="140px"> <img id="loadinicio" class="mx-auto d-block" src="../gui_common/iconos/icono_app.webp" height="120px" width="120px">
<p class="text-center text-white">EMA Libre Cliente <br> (Carpincho) <br> Licencia GPLv3</p> <p class="text-center text-white">EMA Libre Cliente <br> (Carpincho) <br> Licencia GPLv3</p>
</div> </div>
</div> </div>
@ -105,7 +106,7 @@
<div class="col-2 text-center"> <div class="col-2 text-center">
<div id="clima"></div><br> <div id="clima"></div><br>
</div> </div>
<div class="col-2 text-center"> <div class="col-5 text-center">
<div id="DIVModo" class="vertical-align-top" style="visibility: hidden"></div> <div id="DIVModo" class="vertical-align-top" style="visibility: hidden"></div>
<div id="DIVPendientes" class="vertical-align-top" style="visibility: hidden"> <div id="DIVPendientes" class="vertical-align-top" style="visibility: hidden">
<span class='badge text-bg-warning'>Registros Pendientes</span> <span class='badge text-bg-warning'>Registros Pendientes</span>
@ -118,25 +119,25 @@
<div class="container container-fluid"> <div class="container container-fluid">
<ul class="nav nav-tabs nav-inverse nav-pills nav-fill" id="list-tab" role="tablist"> <ul class="nav nav-tabs nav-inverse nav-pills nav-fill" id="list-tab" role="tablist">
<li class="nav-item active "> <li class="nav-item active ">
<a class="nav-link active show" id="inicio-tab" data-bs-toggle="tab" data-bs-target="#inicio" role="tab" aria-controls="inicio" aria-selected="true"><img height="24px" width="24px" src="../gui_common/img/home.webp" aria-controls="inicio"> INICIO</a> <a class="nav-link active show" id="inicio-tab" data-bs-toggle="tab" data-bs-target="#inicio" role="tab" aria-controls="inicio" aria-selected="true"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 10 12 3l9 7M5 9v12h5v-7h4v7h5V9"/></svg><span>Inicio</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="interior-tab" data-bs-toggle="tab" data-bs-target="#interior" role="tab" aria-controls="interior" aria-selected="false"><img height="24px" width="24px" src="../gui_common/img/interior.webp" aria-controls="interior"> INTERIOR</a> <a class="nav-link" id="interior-tab" data-bs-toggle="tab" data-bs-target="#interior" role="tab" aria-controls="interior" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 10 12 3l9 7M5 9v12h14V9M12 10v5m0 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4"/></svg><span>Interior</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="exterior-tab" data-bs-toggle="tab" data-bs-target="#exterior" role="tab" aria-controls="exterior" aria-selected="false"><img height="24px" width="24px" src="../gui_common/img/exterior.webp" aria-controls="exterior"> EXTERIOR</a> <a class="nav-link" id="exterior-tab" data-bs-toggle="tab" data-bs-target="#exterior" role="tab" aria-controls="exterior" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v2m0 14v2M3 12h2m14 0h2M5.6 5.6 7 7m10 10 1.4 1.4M5.6 18.4 7 17M17 7l1.4-1.4M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0"/></svg><span>Exterior</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="estadisticas-tab" data-bs-toggle="tab" data-bs-target="#estadisticas" role="tab" aria-controls="estadisticas" aria-selected="false"><img height="24px" width="24px" src="../gui_common/img/estadistica.png" aria-controls="estadisticas"> ESTADISTICAS</a> <a class="nav-link" id="estadisticas-tab" data-bs-toggle="tab" data-bs-target="#estadisticas" role="tab" aria-controls="estadisticas" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 3v17h17M8 16v-5m5 5V7m5 9V4"/></svg><span>Estadísticas</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="redes-tab" data-bs-toggle="tab" data-bs-target="#redes" role="tab" aria-controls="redes" aria-selected="false"><img height="24px" width="24px" src="../gui_common/img/redes.webp" aria-controls="redes"> REDES</a> <a class="nav-link" id="redes-tab" data-bs-toggle="tab" data-bs-target="#redes" role="tab" aria-controls="redes" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v6M5 20v-6h14v6M12 14v6M9 2h6v5H9zM2 19h6v3H2zM9 19h6v3H9zM16 19h6v3h-6z"/></svg><span>Redes</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="configuracion-tab" data-bs-toggle="tab" data-bs-target="#configuracion" role="tab" aria-controls="configuracion" aria-selected="false"><img height="24px" width="24px" src="../gui_common/img/config.webp" aria-controls="configuracion"> CONFIGURACIÓN</a> <a class="nav-link" id="configuracion-tab" data-bs-toggle="tab" data-bs-target="#configuracion" role="tab" aria-controls="configuracion" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16M8 3v6m8 0v6m-6 0v6"/></svg><span>Configuración</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="contacto-tab" data-bs-toggle="tab" data-bs-target="#contacto" target="_self" role="tab" aria-controls="contacto" aria-selected="false"><img height="24px" width="24px" src="../gui_common/img/about.webp" aria-controls="contacto"> CONTACTO</a> <a class="nav-link" id="contacto-tab" data-bs-toggle="tab" data-bs-target="#contacto" target="_self" role="tab" aria-controls="contacto" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 11v6m0-10v1M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0"/></svg><span>Acerca de</span></a>
</li> </li>
</ul> </ul>
<div class="tab-content ema-panel" id="" style="padding: 15px;padding-bottom:15px;margin-bottom:15px"> <div class="tab-content ema-panel" id="" style="padding: 15px;padding-bottom:15px;margin-bottom:15px">

View File

@ -1,77 +1,36 @@
<section class="ema-home" aria-label="Panel de control">
<div class="container mt-4"> <header class="ema-home-heading">
<h1 class="text-center">Panel de Control</h1> <div><p class="ema-eyebrow">TU ESTACIÓN, EN UN VISTAZO</p><h1>Panel de control</h1></div>
<div class="table-responsive"> <div class="ema-actions">
<table class="table borderless align-middle"> <button type="button" id="botoniniciar" class="btn btn-success" onclick="INICIAR()">Iniciar recolección</button>
<tr class="table-active"> <button type="button" id="botondetener" class="btn btn-outline-danger" onclick="DETENER()">Detener</button>
<!-- Barra Lateral -->
<td rowspan="2" id="barralateral" style="background-color: rgba(251, 251, 251, 0.15)"></td>
<!-- Información General -->
<td>
<h5><b>Información General</b></h5>
<p><img src="../gui_common/img/informacion.png" id="img_nombre" height="25" width="25" alt="Nombre"> Nombre: <span class="badge bg-info text-dark" id="inombre">-</span></p>
<p><img src="../gui_common/img/informacion.png" id="img_so" height="25" width="25" alt="S.O"> S.O: <span class="badge bg-info text-dark" id="iso">-</span></p>
<p><img src="../gui_common/img/informacion.png" id="img_modo" height="25" width="25" alt="Modo"> Modo: <span class="badge bg-info text-dark" id="imodo">-</span></p>
<p><img src="../gui_common/img/informacion.png" height="25" width="25" alt="NW.js"> NW.js:
<span class="badge bg-info text-dark" id="nwjs_v" data-bs-toggle="tooltip" data-bs-placement="right" title="-">-</span>
</p>
</td>
<!-- Tanques del Sistema -->
<td>
<h5><b>Tanques del Sistema</b></h5>
<p><img src="../gui_common/img/archive.webp" height="25" width="25" alt="Diario"> <b>Diario:</b>
<span class="badge bg-info text-dark" id="tamanio_diario">-</span>
</p>
<p><img src="../gui_common/img/archive.webp" height="25" width="25" alt="Pendiente"> <b>Pendiente:</b>
<span class="badge bg-info text-dark" id="tamanio_pendiente">-</span>
<button type="button" class="btn btn-sm btn-warning" onclick="PurgarP()" data-bs-toggle="tooltip" title="Vaciar registros pendientes de redes 'emacenter'.">Purgar</button>
</p>
<p><img src="../gui_common/img/archive.webp" height="25" width="25" alt="Debug"> <b>Debug:</b>
<span class="badge bg-info text-dark" id="tamanio_debug">-</span>
<button type="button" class="btn btn-sm btn-warning" onclick="PurgarD()" data-bs-toggle="tooltip" title="Vaciar registros de logs del sistema.">Purgar</button>
</p>
</td>
<!-- Botón Iniciar -->
<td class="text-center">
<button type="button" id="botoniniciar" class="btn btn-lg btn-success" onclick="INICIAR()">Iniciar</button>
</td>
</tr>
<tr class="table-active">
<!-- Estado Actual -->
<td>
<h5><b>Estado Actual</b></h5>
<p><img id="img_estado" height="25" width="25" alt="Estado"> Estado:
<span class="badge bg-info text-dark" id="estado">-</span></p>
<p><img id="img_estacion" height="25" width="25" alt="Estación"> Estación:
<span class="badge bg-info text-dark" id="iusb">-</span></p>
<p><img id="img_mensaje" height="25" width="25" alt="Mensaje"> Mensaje:
<span class="badge bg-info text-dark" id="mensaje">-</span></p>
<p><img id="img_detalle" height="25" width="25" alt="Detalle"> Detalle:
<span class="badge bg-info text-dark" id="mensajedetalle">N/A</span></p>
</td>
<!-- Información de Ejecución -->
<td>
<h5><b>Información de Ejecución</b></h5>
<p><img src="../gui_common/img/sistema.webp" height="25" width="25" alt="Audio"> <b>Audio:</b>
<span class="badge bg-info text-dark" id="audiomenu" data-bs-toggle="tooltip" title="Estado del audio">-</span></p>
<p><img src="../gui_common/img/sistema.webp" height="25" width="25" alt="AutoInicio"> <b>AutoInicio:</b>
<span class="badge bg-info text-dark" id="autoactivado" data-bs-toggle="tooltip" title="Inicio automático activado.">-</span></p>
<p><img src="../gui_common/img/sistema.webp" height="25" width="25" alt="PID"> <b>PID:</b>
<span class="badge bg-info text-dark" id="node_pid" data-bs-toggle="tooltip" title="PID del proceso.">-</span></p>
<p><img src="../gui_common/img/sistema.webp" height="25" width="25" alt="PPID"> <b>PPID:</b>
<span class="badge bg-info text-dark" id="node_ppid" data-bs-toggle="tooltip" title="PPID del proceso padre.">-</span></p>
</td>
<!-- Botón Detener -->
<td class="text-center">
<button type="button" id="botondetener" class="btn btn-lg btn-danger" onclick="DETENER()">Detener</button>
</td>
</tr>
</table>
</div> </div>
</div> </header>
<div id="inicio-mediciones" class="ema-measurements" aria-label="Últimas mediciones"></div>
<p id="inicio-mediciones-fecha" class="ema-caption">Esperando la primera lectura.</p>
<div class="ema-home-grid">
<article class="ema-info-card"><h2>Estación</h2><dl>
<div><dt>Nombre</dt><dd id="inombre">—</dd></div>
<div><dt>Modo</dt><dd id="imodo">—</dd></div>
<div><dt>Estado</dt><dd><img id="img_estado" width="18" height="18" alt=""> <span id="estado">—</span></dd></div>
</dl></article>
<article class="ema-info-card" id="barralateral"><h2>Conexión y actividad</h2><dl>
<div><dt>Conexión</dt><dd><img id="img_estacion" width="18" height="18" alt=""> <span id="iusb">—</span></dd></div>
<div><dt>Mensaje</dt><dd><img id="img_mensaje" width="18" height="18" alt=""> <span id="mensaje">—</span></dd></div>
<div><dt>Detalle</dt><dd><img id="img_detalle" width="18" height="18" alt=""> <span id="mensajedetalle">—</span></dd></div>
</dl></article>
<article class="ema-info-card"><h2>Registros almacenados</h2><dl>
<div><dt>Diario</dt><dd id="tamanio_diario">—</dd></div>
<div><dt>Pendientes</dt><dd><span id="tamanio_pendiente">—</span> <button type="button" class="btn btn-sm btn-outline-warning" onclick="PurgarP()" title="Vaciar registros pendientes de envío a emacenter">Purgar pendientes</button></dd></div>
<div><dt>Logs</dt><dd><span id="tamanio_debug">—</span> <button type="button" class="btn btn-sm btn-outline-warning" onclick="PurgarD()" title="Vaciar los logs del sistema">Purgar logs</button></dd></div>
</dl></article>
</div>
<details class="ema-runtime"><summary>Opciones y detalles de ejecución</summary><dl>
<div><dt>Audio</dt><dd id="audiomenu">—</dd></div>
<div><dt>Inicio automático</dt><dd id="autoactivado">—</dd></div>
<div><dt>Sistema operativo</dt><dd id="iso">—</dd></div>
<div><dt>NW.js</dt><dd id="versiones"><span id="nwjs_v">—</span></dd></div>
<div><dt>PID</dt><dd id="node_pid">—</dd></div>
<div><dt>PPID</dt><dd id="node_ppid">—</dd></div>
</dl></details>
</section>

View File

@ -185,7 +185,7 @@ function INICIAR () {
$('#signal').addClass('parpadea'); $('#signal').addClass('parpadea');
} }
if (_IS_NW) { createTray(); tray.icon = iconoEncendido; } if (_IS_NW) { createTray(); tray.icon = iconoEncendido; }
$('td#barralateral').css('background-color', '#11881161'); $('#barralateral').css('background-color', '#11881161');
$('#estado').html('En Linea'); $('#estado').html('En Linea');
$('#mensaje').html('Encendido'); $('#mensaje').html('Encendido');
$('#botoniniciar').attr('disabled', true); $('#botoniniciar').attr('disabled', true);
@ -249,7 +249,7 @@ function DETENER () {
clearInterval(intervaloBarra); clearInterval(intervaloBarra);
killAllTimers(); killAllTimers();
if (_IS_NW) { createTray(); tray.icon = iconoApagado; } if (_IS_NW) { createTray(); tray.icon = iconoApagado; }
$('td#barralateral').css('background-color', '#ff000030'); $('#barralateral').css('background-color', '#ff000030');
$('#estado').html('Fuera de Linea'); $('#estado').html('Fuera de Linea');
$('#img_estado').attr('src', iconoNeutro); $('#img_estado').attr('src', iconoNeutro);
$('#mensaje').html('EMA Libre se encuentra detenida'); $('#mensaje').html('EMA Libre se encuentra detenida');
@ -912,6 +912,7 @@ async function MenuGUI (elemento) {
$('#nwjs_v').html(version_node); $('#nwjs_v').html(version_node);
} }
EstadoMenuInicio(); EstadoMenuInicio();
EstadoUI.renderHome();
$('#interior').empty();$('#exterior').empty();$('#contacto').empty();$('#configuracion').empty();$('#redes').empty();$('#estadisticas').empty(); $('#interior').empty();$('#exterior').empty();$('#contacto').empty();$('#configuracion').empty();$('#redes').empty();$('#estadisticas').empty();
break; break;
case 'interior': case 'interior':

View File

@ -49,7 +49,7 @@
} }
.container { .container {
max-width: 1199px !important; max-width: 95% !important;
} }
h1 { h1 {
@ -535,7 +535,7 @@ table.dataTable.table-bordered {
/* Layout stays readable at narrow widths without automatic zoom. */ /* Layout stays readable at narrow widths without automatic zoom. */
#principal { width: 100%; padding-bottom: 2rem; } #principal { width: 100%; padding-bottom: 2rem; }
#principal > .navbar { gap: .5rem; flex-wrap: wrap; } #principal > .navbar { gap: .5rem; flex-wrap: wrap; }
#principal .container { width: 100%; max-width: 1800px; } #principal .container { width: 100%; max-width: 95%; }
#config-form .row { row-gap: 1rem; } #config-form .row { row-gap: 1rem; }
#config-form input, #config-form select { max-width: 100%; min-width: 0; } #config-form input, #config-form select { max-width: 100%; min-width: 0; }
#config-form label { display: inline-block; margin-bottom: .35rem; } #config-form label { display: inline-block; margin-bottom: .35rem; }

View File

@ -0,0 +1,117 @@
/* Paleta y jerarquía compartidas por web y escritorio. */
:root {
--ema-surface: #142b3e;
--ema-raised: #1b3549;
--ema-border: #426075;
--ema-text: #edf5fa;
--ema-muted: #b5cbd9;
--ema-accent: #85e1de;
}
body, body.dia, body.dia_lluvia, body.noche, body.noche_lluvia {
font-family: system-ui, -apple-system, "Segoe UI", sans-serif !important;
color: var(--ema-text); font-size: 15px; line-height: 1.5;
background-attachment: fixed;
}
body h1, body .h1 {font-size: clamp(1.45rem, 2vw, 1.9rem) !important; font-weight: 650;}
body h2, body .h2 {font-size: 1.2rem !important; font-weight: 650;}
body h3, body .h3 {font-size: 1.1rem !important;}
body h4, body .h4, body h5, body .h5, body h6, body .h6 {font-size: 1rem !important;}
body p, body ul, body table, body th, body td {font-size: .925rem !important;}
body a, body b, body p, body h1, body h2, body h3, body h4, body h5, body h6 {
font-family: inherit !important;
}
body .container {max-width: 95% !important;}
#principal > .navbar {min-height: 44px; padding: .5rem 1rem; background: #102437;}
.ema-panel {background: rgba(15, 34, 49, .96) !important; border: 1px solid var(--ema-border); border-radius: 12px; padding: 1.25rem;}
#list-tab {gap: .4rem; padding: .5rem; border: 1px solid var(--ema-border); border-radius: 12px 12px 0 0; background: #102437;}
#list-tab .nav-item {min-width: 110px;}
#list-tab .nav-link {
display: flex; align-items: center; justify-content: center; gap: .55rem;
min-height: 48px; padding: .65rem .8rem; border: 1px solid transparent;
border-radius: 8px; color: #d3e3ed !important; cursor: pointer !important;
font-weight: 600; text-decoration: none;
}
#list-tab .nav-link:hover {background: #25465b; color: #fff !important;}
#list-tab .nav-link.active {background: #1a606f !important; border-color: #85e1de; color: #fff !important; box-shadow: inset 0 -3px 0 #85e1de;}
.ema-nav-icon {width: 22px; height: 22px; flex: 0 0 22px;}
:where(button, a, select, input, summary):focus-visible {outline: 3px solid #85e1de !important; outline-offset: 3px;}
.btn {border-radius: 7px; font-weight: 600; padding: .5rem .9rem;}
.btn-sm {padding: .3rem .6rem;}
.btn-primary {--bs-btn-bg:#1a606f; --bs-btn-border-color:#438c96; --bs-btn-hover-bg:#237889; --bs-btn-hover-border-color:#85e1de;}
.btn-outline-danger {--bs-btn-color:#ffb8ba; --bs-btn-border-color:#bf6971;}
.btn-outline-warning {--bs-btn-color:#ffe0a3; --bs-btn-border-color:#997b49;}
.alert-info {--bs-alert-bg:#1a606f; --bs-alert-color:#fff; --bs-alert-border-color:#438c96;}
.alert-warning {--bs-alert-bg:#574422; --bs-alert-color:#fff1cc; --bs-alert-border-color:#9e8046;}
.alert-danger {--bs-alert-bg:#622f3b; --bs-alert-color:#ffe2e7; --bs-alert-border-color:#a76270;}
.alert-success {--bs-alert-bg:#1c5545; --bs-alert-color:#d9fff0; --bs-alert-border-color:#4b9b81;}
.estado-estacion {
position: sticky; top: 44px; z-index: 1010;
display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
gap: .6rem; padding: .65rem; margin: 0 0 1rem;
background: #102437; border-bottom: 1px solid var(--ema-border);
box-shadow: 0 5px 14px #0003;
}
.estado-tarjeta {padding: .55rem .75rem; gap: .3rem; background: var(--ema-surface); border: 1px solid var(--ema-border); border-radius: 8px;}
.estado-tarjeta strong {font-size: .78rem; color: var(--ema-muted); letter-spacing: .04em; text-transform: uppercase;}
.estado-tarjeta .badge {font-size: .8rem; line-height: 1.35; align-self: flex-start;}
.estado-tarjeta small {font-size: .75rem; line-height: 1.35;}
.ema-state-symbol {margin-right:.4em;}
.ema-home-heading {display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;}
body .ema-eyebrow {color: var(--ema-accent) !important; font-size:.72rem !important; letter-spacing:.12em; margin-bottom:.3rem; font-weight:700;}
.ema-home h1 {margin:0;}
.ema-actions {display:flex; flex-wrap:wrap; gap:.6rem;}
.ema-measurements {display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:1rem;}
.ema-measurement {display:flex; flex-direction:column; text-align:left; min-width:0; padding:1.2rem; gap:.45rem; border:1px solid var(--ema-border); border-top:3px solid var(--ema-accent); border-radius:10px; background:var(--ema-raised); color:var(--ema-text); cursor:pointer;}
.ema-measurement:hover {background:#24465c; border-color:var(--ema-accent);}
.ema-measurement-label {font-size:.875rem; color:var(--ema-muted);}
.ema-measurement-value {font-size:clamp(1.8rem,3vw,2.5rem); font-weight:700; line-height:1.25; font-variant-numeric:tabular-nums;}
.ema-measurement-value small {font-size:1rem; font-weight:400; margin-left:.35rem; color:var(--ema-muted);}
.ema-measurement-link {font-size:.75rem; color:var(--ema-accent);}
body .ema-caption {color:var(--ema-muted) !important; font-size:.8rem !important; margin:.75rem 0 1.25rem;}
.ema-home-grid {display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:1rem;}
.ema-info-card {background:var(--ema-surface); border:1px solid var(--ema-border); border-radius:10px; padding:1.1rem;}
.ema-info-card h2 {margin-bottom:1rem;}
.ema-home dl {margin:0;}
.ema-home dl > div {display:flex; justify-content:space-between; align-items:baseline; gap:.75rem; padding:.55rem 0; border-bottom:1px solid #42607566;}
.ema-home dl > div:last-child {border-bottom:0;}
.ema-home dt {font-size:.8rem; color:var(--ema-muted); font-weight:500; flex-shrink:0;}
.ema-home dd {font-size:.875rem; text-align:right; margin:0; overflow-wrap:anywhere;}
.ema-home dd img:not([src]) {display:none;}
.ema-home dd .btn {display:block; margin:.4rem 0 0 auto;}
.ema-runtime {margin-top:1rem; padding:1rem; border:1px solid var(--ema-border); border-radius:10px;}
.ema-runtime summary {cursor:pointer; color:var(--ema-muted); font-weight:600;}
.ema-runtime dl {display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:0 2rem; margin-top:.6rem;}
.table, table.dataTable {
--bs-table-border-color:var(--ema-border); --bs-table-bg:transparent;
--bs-table-color:var(--ema-text); --bs-table-hover-bg:#24495e;
--bs-table-striped-bg:#ffffff06; font-variant-numeric:tabular-nums;
}
body .table th, body .table td {padding:.7rem .85rem; border-color:var(--ema-border) !important; vertical-align:middle;}
.table thead th {position:sticky; top:0; z-index:2; background:#203e53; white-space:nowrap; border-bottom:2px solid #65b9c1 !important;}
.table tbody tr:hover > * {background-color:#24495e;}
.table tbody tr.ema-selected > *, .table tbody tr:focus-within > * {background-color:#2c536a;}
.table .ema-number, .table .dt-body-right {text-align:right !important; font-variant-numeric:tabular-nums;}
.ema-table-scroll {max-height:65vh; overflow:auto; scrollbar-color:#6a96ac #142b3e;}
.dataTables_scrollHead {background:#203e53; border-radius:8px 8px 0 0;}
.dataTables_scrollBody {scrollbar-color:#6a96ac #142b3e;}
.dataTables_wrapper .dt-buttons {display:flex; gap:.5rem; flex-wrap:wrap; margin-bottom:.75rem;}
.dataTables_wrapper .dataTables_filter {margin-bottom:.75rem;}
.dataTables_wrapper .form-control, .dataTables_wrapper .form-select {background:#f1f6f9; color:#142b3e; border:1px solid #82a1b5;}
.estadisticas-panel {background:var(--ema-surface);}
@media (max-width:1000px) {
.ema-measurements {grid-template-columns:repeat(2,minmax(0,1fr));}
.ema-home-grid {grid-template-columns:1fr;}
.ema-runtime dl {grid-template-columns:repeat(2,minmax(0,1fr));}
}
@media (max-width:767px) {
.estado-estacion {top:44px; gap:.3rem; padding:.4rem;}
.estado-tarjeta {padding:.4rem;}
.estado-tarjeta small {display:none;}
.estado-tarjeta strong {font-size:.65rem; letter-spacing:0;}
.estado-tarjeta .badge {font-size:.7rem;}
#list-tab .nav-item {min-width:90px;}
#principal #list-tab .nav-link {font-size:.8rem;}
.ema-measurement {padding:.85rem;}
.ema-panel {padding:.8rem !important;}
}
@media (prefers-reduced-motion:reduce) {*, *::before, *::after {scroll-behavior:auto !important; transition:none !important;}}

View File

@ -113,7 +113,7 @@ const EstadisticasUI = (() => {
} }
target.append(element('p', `${result.dated} registros · ${result.changed} con cambios · ${result.first} — ${result.last}`)); target.append(element('p', `${result.dated} registros · ${result.changed} con cambios · ${result.first} — ${result.last}`));
if (result.discarded) target.append(element('p', `Se omitieron ${result.discarded} registros sin fecha válida.`, 'alert alert-warning')); if (result.discarded) target.append(element('p', `Se omitieron ${result.discarded} registros sin fecha válida.`, 'alert alert-warning'));
const tableBox = element('div', undefined, 'table-responsive'); const tableBox = element('div', undefined, 'table-responsive ema-table-scroll');
const table = element('table', undefined, 'table table-bordered table-sm'); const table = element('table', undefined, 'table table-bordered table-sm');
table.append(element('caption', 'Resumen de las muestras disponibles. Los acumulados de lluvia no se suman. — indica un dato ausente.')); table.append(element('caption', 'Resumen de las muestras disponibles. Los acumulados de lluvia no se suman. — indica un dato ausente.'));
const header = element('tr'); const header = element('tr');
@ -122,7 +122,7 @@ const EstadisticasUI = (() => {
const body = element('tbody'); const body = element('tbody');
for (const series of result.series) { for (const series of result.series) {
const row = element('tr'); const row = element('tr');
for (const value of [series.label, series.unit, series.count, format(series.min), format(series.mean), format(series.max), format(series.last)]) row.append(element('td', value)); for (const [index, value] of [series.label, series.unit, series.count, format(series.min), format(series.mean), format(series.max), format(series.last)].entries()) row.append(element('td', value, index >= 2 ? 'ema-number' : undefined));
body.append(row); body.append(row);
} }
table.append(body); tableBox.append(table); target.append(tableBox); table.append(body); tableBox.append(table); target.append(tableBox);

View File

@ -1,12 +1,55 @@
const EstadoUI = (() => { const EstadoUI = (() => {
let timer, started = false; let timer, started = false, latest = {};
function date(value, utc) { function date(value, utc) {
const match = String(value || '').match(/^(\d{2})\/(\d{2})\/(\d{4})[ T](\d{2}):(\d{2}):(\d{2})/); const match = String(value || '').match(/^(\d{2})\/(\d{2})\/(\d{4})[ T](\d{2}):(\d{2}):(\d{2})/);
if (!match) return Date.parse(value); if (!match) return Date.parse(value);
if (Number.isFinite(Number(utc))) return Date.UTC(+match[3], +match[2] - 1, +match[1], +match[4], +match[5], +match[6]) - Number(utc) * 3600000; if (Number.isFinite(Number(utc))) return Date.UTC(+match[3], +match[2] - 1, +match[1], +match[4], +match[5], +match[6]) - Number(utc) * 3600000;
return new Date(+match[3], +match[2] - 1, +match[1], +match[4], +match[5], +match[6]).getTime(); return new Date(+match[3], +match[2] - 1, +match[1], +match[4], +match[5], +match[6]).getTime();
} }
function renderHome(data = latest) {
const target = document.getElementById('inicio-mediciones');
if (!target) return;
const record = data.record || {};
const imperial = data.system && data.system !== 'SI';
const metrics = [
['Temperatura exterior', 'temperatura_externa', imperial ? '°F' : '°C', v => imperial ? v * 9 / 5 + 32 : v],
['Humedad exterior', 'humedad_externa', '%', v => v],
['Velocidad del viento', 'viento_velocidad', imperial ? 'mph' : 'km/h', v => imperial ? v * 0.621371192 : v],
['Lluvia del día', 'lluvia_acu_diaria', 'mm', v => v]
];
// Actualizar el contenido sin reemplazar los botones: conservar foco al refrescar.
metrics.forEach(([label, key, unit, convert]) => {
let button = target.querySelector('[data-medicion="' + key + '"]');
if (!button) {
button = document.createElement('button'); button.type = 'button';
button.className = 'ema-measurement'; button.dataset.medicion = key;
const heading = document.createElement('span'); heading.className = 'ema-measurement-label'; heading.textContent = label;
const value = document.createElement('span'); value.className = 'ema-measurement-value';
const link = document.createElement('span'); link.className = 'ema-measurement-link'; link.textContent = 'Ver detalle exterior →';
button.append(heading, value, link);
button.onclick = () => document.getElementById('exterior-tab')?.click();
target.append(button);
}
const raw = record[key];
const valid = (typeof raw === 'number' || typeof raw === 'string' && raw.trim() !== '') && Number.isFinite(Number(raw));
const value = valid ? convert(Number(raw)).toLocaleString('es-AR', {maximumFractionDigits:1}) : '—';
const display = button.querySelector('.ema-measurement-value');
const units = document.createElement('small'); units.textContent = unit;
display.replaceChildren(document.createTextNode(value), units);
button.setAttribute('aria-label', label + ': ' + (valid ? value + ' ' + unit : 'sin datos') + '. Ver detalle exterior');
});
const note = document.getElementById('inicio-mediciones-fecha');
if (note) {
const age = Date.now() - date(record.fecha, data.utc);
const stale = Number.isFinite(age) && age > Math.max(120, Number(data.interval || 60) * 3) * 1000;
note.textContent = data.error ? 'No se pudo actualizar la lectura. Revisá la conexión con el servicio.' : record.fecha
? 'Última lectura: ' + record.fecha + (stale ? ' · Dato no reciente: revisá la conexión.' : '')
: 'Esperando la primera lectura. Los datos ausentes se muestran con —.';
}
}
function render(data) { function render(data) {
latest = data;
renderHome(data);
const target = document.getElementById('estado-estacion'); const target = document.getElementById('estado-estacion');
if (!target) return; if (!target) return;
target.replaceChildren(); target.replaceChildren();
@ -14,7 +57,8 @@ const EstadoUI = (() => {
const box = document.createElement('div'); box.className = 'estado-tarjeta'; const box = document.createElement('div'); box.className = 'estado-tarjeta';
const heading = document.createElement('strong'); heading.textContent = title; const heading = document.createElement('strong'); heading.textContent = title;
const badge = document.createElement('span'); badge.className = 'badge text-bg-' + level; badge.textContent = text; const badge = document.createElement('span'); badge.className = 'badge text-bg-' + level; badge.textContent = text;
const help = document.createElement('small'); help.textContent = detail; const symbol = document.createElement('span'); symbol.className = 'ema-state-symbol'; symbol.setAttribute('aria-hidden', 'true'); symbol.textContent = {success:'✓', warning:'⚠', danger:'!', secondary:'•'}[level]; badge.prepend(symbol);
const help = document.createElement('small'); help.textContent = detail; box.title = detail;
box.append(heading, badge, help); box.append(heading, badge, help);
if (action) { if (action) {
const button = document.createElement('button'); button.className = 'btn btn-sm btn-outline-light'; button.textContent = action.label; button.onclick = action.run; box.append(button); const button = document.createElement('button'); button.className = 'btn btn-sm btn-outline-light'; button.textContent = action.label; button.onclick = action.run; box.append(button);
@ -51,10 +95,10 @@ const EstadoUI = (() => {
if (local) { state = EMA.Estado(); record = EMA.UltimoRegistro(); publications = EMA.UltimoPublicado(); sensors = EMA.EstadoSensores(); } if (local) { state = EMA.Estado(); record = EMA.UltimoRegistro(); publications = EMA.UltimoPublicado(); sensors = EMA.EstadoSensores(); }
else [state, record, publications, sensors] = await Promise.all(['/EMA_Estado','/UltimoRegistro','/UltimoPublicado','/EstadoSensores'].map(url => $.ajax({url, timeout:5000}))); else [state, record, publications, sensors] = await Promise.all(['/EMA_Estado','/UltimoRegistro','/UltimoPublicado','/EstadoSensores'].map(url => $.ajax({url, timeout:5000})));
if (config.ConfGeneral.Modo === 'Visor' && typeof UltimoReg !== 'undefined') record = UltimoReg; if (config.ConfGeneral.Modo === 'Visor' && typeof UltimoReg !== 'undefined') record = UltimoReg;
render({mode:config.ConfGeneral.Modo, interval:config.ConfGeneral.Tiempo_de_Recoleccion, utc:config.ConfGeneral.UTC, state, record, publications, sensors}); render({mode:config.ConfGeneral.Modo, system:config.ConfGeneral.SistemaMetrico, interval:config.ConfGeneral.Tiempo_de_Recoleccion, utc:config.ConfGeneral.UTC, state, record, publications, sensors});
} catch (error) {render({error:'No se pudo consultar el estado. Comprobá que el servicio esté disponible.'});} } catch (error) {render({error:'No se pudo consultar el estado. Comprobá que el servicio esté disponible.'});}
if (started) timer = setTimeout(refresh, 10000); if (started) timer = setTimeout(refresh, 10000);
} }
function start() { if (!started) {started = true; refresh();} } function start() { if (!started) {started = true; refresh();} }
return {start, refresh, render}; return {start, refresh, render, renderHome};
})(); })();

View File

@ -8,7 +8,21 @@ const TablaUI = (() => {
{extend: 'csv', text: '💾 Exportar CSV', className: 'btn-primary'} {extend: 'csv', text: '💾 Exportar CSV', className: 'btn-primary'}
]; ];
} }
function options() { return {dom: 'Bfrtip', buttons: buttons(), language: {url: languageURL}, pageLength: 8, scrollX: true, autoWidth: false, destroy: true}; } function decorateRow(row) {
row.tabIndex = 0;
for (const cell of row.cells) {
cell.classList.toggle('ema-number', /^[+-]?\d+(?:[.,]\d+)*(?:\s*%)?$/.test(cell.textContent.trim()));
}
const select = event => {
if (event.target.closest('button, a, input, select')) return;
if (event.type === 'keydown' && !['Enter', ' '].includes(event.key)) return;
if (event.type === 'keydown') event.preventDefault();
for (const sibling of row.parentNode.rows) sibling.classList.remove('ema-selected');
row.classList.add('ema-selected');
};
row.onclick = select; row.onkeydown = select;
}
function options() { return {dom: 'Bfrtip', buttons: buttons(), language: {url: languageURL}, pageLength: 8, scrollX: true, scrollY: '55vh', scrollCollapse: true, rowCallback: decorateRow, autoWidth: false, destroy: true}; }
return {buttons, options}; return {buttons, options};
})(); })();
function MostrarTabla(datos, div, tabla) { function MostrarTabla(datos, div, tabla) {

View File

@ -14,6 +14,7 @@
<!-- Estilo principal css --> <!-- Estilo principal css -->
<link rel="stylesheet" type="text/css" href="gui_common/css/style.css"> <link rel="stylesheet" type="text/css" href="gui_common/css/style.css">
<link rel="stylesheet" href="gui_common/css/visual.css">
<!-- Libreria bootstrap/jquery para graficos --> <!-- Libreria bootstrap/jquery para graficos -->
<script type="text/javascript" src="js/jquery.min.js"></script> <script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.bundle.min.js"></script> <script type="text/javascript" src="js/bootstrap.bundle.min.js"></script>
@ -87,7 +88,7 @@
</div> </div>
<div class="col-3" align="center" style="vertical-align:top;"></div> <div class="col-3" align="center" style="vertical-align:top;"></div>
<div class="col-2" align="center"><div id="clima"> </div><br></div> <div class="col-2" align="center"><div id="clima"> </div><br></div>
<div class="col-2" align="center"> <div class="col-5" align="center">
<div id="DIVModo" style="vertical-align:top;visibility: hidden"> </div> <div id="DIVModo" style="vertical-align:top;visibility: hidden"> </div>
<div id="DIVPendientes" style="vertical-align:top;visibility: hidden"> <div id="DIVPendientes" style="vertical-align:top;visibility: hidden">
<span class='badge text-bg-warning'>Registros Pendientes</span> <span class='badge text-bg-warning'>Registros Pendientes</span>
@ -101,25 +102,25 @@
<div class="container container-fluid"> <div class="container container-fluid">
<ul class="nav nav-tabs nav-inverse nav-pills nav-fill" id="list-tab" role="tablist"> <ul class="nav nav-tabs nav-inverse nav-pills nav-fill" id="list-tab" role="tablist">
<li class="nav-item active "> <li class="nav-item active ">
<a class="nav-link active show" id="inicio-tab" data-bs-toggle="tab" data-bs-target="#inicio" role="tab" aria-controls="inicio" aria-selected="true"><img height="24px" width="24px" src="gui_common/img/home.webp" aria-controls="inicio"> INICIO</a> <a class="nav-link active show" id="inicio-tab" data-bs-toggle="tab" data-bs-target="#inicio" role="tab" aria-controls="inicio" aria-selected="true"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 10 12 3l9 7M5 9v12h5v-7h4v7h5V9"/></svg><span>Inicio</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="interior-tab" data-bs-toggle="tab" data-bs-target="#interior" role="tab" aria-controls="interior" aria-selected="false"><img height="24px" width="24px" src="gui_common/img/interior.webp" aria-controls="interior"> INTERIOR</a> <a class="nav-link" id="interior-tab" data-bs-toggle="tab" data-bs-target="#interior" role="tab" aria-controls="interior" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 10 12 3l9 7M5 9v12h14V9M12 10v5m0 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4"/></svg><span>Interior</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="exterior-tab" data-bs-toggle="tab" data-bs-target="#exterior" role="tab" aria-controls="exterior" aria-selected="false"><img height="24px" width="24px" src="gui_common/img/exterior.webp" aria-controls="exterior"> EXTERIOR</a> <a class="nav-link" id="exterior-tab" data-bs-toggle="tab" data-bs-target="#exterior" role="tab" aria-controls="exterior" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v2m0 14v2M3 12h2m14 0h2M5.6 5.6 7 7m10 10 1.4 1.4M5.6 18.4 7 17M17 7l1.4-1.4M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0"/></svg><span>Exterior</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="estadisticas-tab" data-bs-toggle="tab" data-bs-target="#estadisticas" role="tab" aria-controls="estadisticas" aria-selected="false"><img height="24px" width="24px" src="gui_common/img/estadistica.png" aria-controls="estadisticas"> ESTADISTICAS</a> <a class="nav-link" id="estadisticas-tab" data-bs-toggle="tab" data-bs-target="#estadisticas" role="tab" aria-controls="estadisticas" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 3v17h17M8 16v-5m5 5V7m5 9V4"/></svg><span>Estadísticas</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="redes-tab" data-bs-toggle="tab" data-bs-target="#redes" role="tab" aria-controls="redes" aria-selected="false"><img height="24px" width="24px" src="gui_common/img/redes.webp" aria-controls="redes"> REDES</a> <a class="nav-link" id="redes-tab" data-bs-toggle="tab" data-bs-target="#redes" role="tab" aria-controls="redes" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v6M5 20v-6h14v6M12 14v6M9 2h6v5H9zM2 19h6v3H2zM9 19h6v3H9zM16 19h6v3h-6z"/></svg><span>Redes</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="configuracion-tab" data-bs-toggle="tab" data-bs-target="#configuracion" role="tab" aria-controls="configuracion" aria-selected="false"><img height="24px" width="24px" src="gui_common/img/config.webp" aria-controls="configuracion"> CONFIGURACIÓN</a> <a class="nav-link" id="configuracion-tab" data-bs-toggle="tab" data-bs-target="#configuracion" role="tab" aria-controls="configuracion" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16M8 3v6m8 0v6m-6 0v6"/></svg><span>Configuración</span></a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" id="contacto-tab" data-bs-toggle="tab" data-bs-target="#contacto" target="_self" role="tab" aria-controls="contacto" aria-selected="false"><img height="24px" width="24px" src="gui_common/img/about.webp" aria-controls="contacto"> CONTACTO</a> <a class="nav-link" id="contacto-tab" data-bs-toggle="tab" data-bs-target="#contacto" target="_self" role="tab" aria-controls="contacto" aria-selected="false"><svg class="ema-nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 11v6m0-10v1M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0"/></svg><span>Acerca de</span></a>
</li> </li>
</ul> </ul>
<div class="tab-content ema-panel" id="" style="padding: 15px;padding-bottom:15px;margin-bottom:15px"> <div class="tab-content ema-panel" id="" style="padding: 15px;padding-bottom:15px;margin-bottom:15px">

View File

@ -1,49 +1,36 @@
<br> <section class="ema-home" aria-label="Panel de control">
<h1>Panel de Control</h1> <header class="ema-home-heading">
<div> <div><p class="ema-eyebrow">TU ESTACIÓN, EN UN VISTAZO</p><h1>Panel de control</h1></div>
<table class="table borderless"> <div class="ema-actions">
<tr class="active"> <button type="button" id="botoniniciar" class="btn btn-success" onclick="INICIAR()">Iniciar recolección</button>
<td rowspan="2" id="barralateral" style="background-color: rgba(251, 251, 251, 0.15)"></td> <button type="button" id="botondetener" class="btn btn-outline-danger" onclick="DETENER()">Detener</button>
<td> </div>
<h5><b>Información General<b><h5> </header>
<h5><img src="gui_common/img/informacion.png" id="img_nombre" height="25" width="25"> Nombre: <span class="badge badge-info" style="font-size:small" id="inombre">-</span></h5> <div id="inicio-mediciones" class="ema-measurements" aria-label="Últimas mediciones"></div>
<h5><img src="gui_common/img/informacion.png" id="img_so" height="25" width="25"> S.O: <span class="badge badge-info" style="font-size:small" id="iso">-</span></h5> <p id="inicio-mediciones-fecha" class="ema-caption">Esperando la primera lectura.</p>
<h5><img src="gui_common/img/informacion.png" id="img_modo" height="25" width="25"> Modo: <span class="badge badge-info" style="font-size:small" id="imodo">-</span></h5> <div class="ema-home-grid">
<h5><img src="gui_common/img/informacion.png" height="25" width="25"> Node.js: <span id='versiones' class="badge badge-info" style="font-size:small" data-bs-toggle="tooltip" data-bs-placement="right" data-bs-html="true" title="-"><span id="nwjs_v">-</span></h5> <article class="ema-info-card"><h2>Estación</h2><dl>
</td> <div><dt>Nombre</dt><dd id="inombre">—</dd></div>
<td WIDTH = "1%" rowspan="2"></td> <div><dt>Modo</dt><dd id="imodo">—</dd></div>
<div><dt>Estado</dt><dd><img id="img_estado" width="18" height="18" alt=""> <span id="estado">—</span></dd></div>
<td> </dl></article>
<h5><b>Tanques del Sistema</b></h5> <article class="ema-info-card" id="barralateral"><h2>Conexión y actividad</h2><dl>
<h5><img src="gui_common/img/archive.webp" height="25" width="25"><b>Diario: </b> <span class="badge badge-info" style="font-size:small" id="tamanio_diario">-</span> </h5> <div><dt>Conexión</dt><dd><img id="img_estacion" width="18" height="18" alt=""> <span id="iusb">—</span></dd></div>
<h5><img src="gui_common/img/archive.webp" height="25" width="25"><b>Pendiente:</b> <span class="badge badge-info" style="font-size:small" id="tamanio_pendiente">-</span> <button type="button" class="btn btn-sm btn-warning" onclick="PurgarP()" data-bs-toggle="tooltip" data-bs-placement="right" title="Si presiona este botón de purgar, usted vaciará el archivo que almacena los registros pendientes de enviar, esto solo aplica para registros pendientes de enviar de redes de tipo 'emacenter'.">Purgar</button ></h5> <div><dt>Mensaje</dt><dd><img id="img_mensaje" width="18" height="18" alt=""> <span id="mensaje">—</span></dd></div>
<h5><img src="gui_common/img/archive.webp" height="25" width="25"><b>Debug: </b> <span class="badge badge-info" style="font-size:small" id="tamanio_debug">-</span> <button type="button" class="btn btn-sm btn-warning" onclick="PurgarD()" data-bs-toggle="tooltip" data-bs-placement="right" title="Si presiona este botón de purgar, usted vaciará el archivo que almacena los registros de logs del sistema." >Purgar</button > </h5> <div><dt>Detalle</dt><dd><img id="img_detalle" width="18" height="18" alt=""> <span id="mensajedetalle">—</span></dd></div>
</dl></article>
</td> <article class="ema-info-card"><h2>Registros almacenados</h2><dl>
<div><dt>Diario</dt><dd id="tamanio_diario">—</dd></div>
<td style="display:table-cell; vertical-align:middle; text-align:center"> <div><dt>Pendientes</dt><dd><span id="tamanio_pendiente">—</span> <button type="button" class="btn btn-sm btn-outline-warning" onclick="PurgarP()" title="Vaciar registros pendientes de envío a emacenter">Purgar pendientes</button></dd></div>
<button type="button" id="botoniniciar" class="btn btn-lg btn-success" onclick="INICIAR()" role="button">Iniciar</button> <div><dt>Logs</dt><dd><span id="tamanio_debug">—</span> <button type="button" class="btn btn-sm btn-outline-warning" onclick="PurgarD()" title="Vaciar los logs del sistema">Purgar logs</button></dd></div>
</td> </dl></article>
</tr> </div>
<details class="ema-runtime"><summary>Opciones y detalles de ejecución</summary><dl>
<tr class="active"> <div><dt>Audio</dt><dd id="audiomenu">—</dd></div>
<td> <div><dt>Inicio automático</dt><dd id="autoactivado">—</dd></div>
<h5><b>Estado Actual</b></h5> <div><dt>Sistema operativo</dt><dd id="iso">—</dd></div>
<h5><img id="img_estado" height="25" width="25"> Estado: <span class="badge badge-info" style="font-size:small" id="estado">-</span></h5> <div><dt>Node.js</dt><dd id="versiones"><span id="nwjs_v">—</span></dd></div>
<h5><img id="img_estacion" height="25" width="25"> Estación:<span class="badge badge-info" style="font-size:small" id="iusb" >-</span></h5> <div><dt>PID</dt><dd id="node_pid">—</dd></div>
<h5><img id="img_mensaje" height="25" width="25"> Mensaje: <span class="badge badge-info" style="font-size:small" id="mensaje">-</span></h5> <div><dt>PPID</dt><dd id="node_ppid">—</dd></div>
<h5><img id="img_detalle" height="25" width="25"> Detalle: <span class="badge badge-info" style="font-size:small" id="mensajedetalle" >N/A</span></h5> </dl></details>
</td> </section>
<td>
<h5><b>Información de Ejecución</b> </h5>
<h5><img src="gui_common/img/sistema.webp" height="25" width="25"><b>Audio: </b> <span class="badge badge-info" id="audiomenu" data-bs-toggle="tooltip" data-bs-placement="right" title="Indica si esta activado el audio" style="font-size:small">-</span> </h5>
<h5><img src="gui_common/img/sistema.webp" height="25" width="25"><b>AutoInicio: </b> <span class="badge badge-info" id="autoactivado" data-bs-toggle="tooltip" data-bs-placement="right" title="Indica si el inicio automatico esta activado." style="font-size:small">-</span> </h5>
<h5><img src="gui_common/img/sistema.webp" height="25" width="25"><b>PID: </b> <span class="badge badge-info" id="node_pid" data-bs-toggle="tooltip" data-bs-placement="right" title="Numero del Identificado de Proceso (PID) de la aplicación Ema Libre." style="font-size:small">-</span> </h5>
<h5><img src="gui_common/img/sistema.webp" height="25" width="25"><b>PPID: </b> <span class="badge badge-info" id="node_ppid" data-bs-toggle="tooltip" data-bs-placement="right" title="Numero del Identificado de Proceso Padre (PPID) de la aplicación Ema Libre." style="font-size:small">-</span> </h5>
</td>
<td style="display:table-cell; vertical-align:middle; text-align:center">
<button id="botondetener" class="btn btn-lg btn-danger" onclick="DETENER()" role="button">Detener</button >
</td>
</tr>
</table>
</div>

View File

@ -57,3 +57,81 @@
29/09/2026 23:52:16 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui 29/09/2026 23:52:16 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
29/09/2026 23:52:16 - 0|CORE|✅ Modulo node-hid cargado correctamente. 29/09/2026 23:52:16 - 0|CORE|✅ Modulo node-hid cargado correctamente.
29/09/2026 23:52:16 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web. 29/09/2026 23:52:16 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:03:28 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:03:28 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:03:28 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:05:39 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:05:39 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:05:39 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:05:49 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:05:49 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:05:49 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:06:42 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:06:42 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:06:42 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:07:32 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:07:32 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:07:32 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:13:25 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:13:25 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:13:25 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:14:15 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:14:15 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:14:15 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:14:18 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:15:21 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:15:21 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:15:21 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:15:24 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:15:33 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:15:33 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:15:33 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:15:37 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:16:00 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:16:00 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:16:00 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:16:05 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:16:14 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:16:14 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:16:14 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:16:17 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:17:40 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:17:40 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:17:40 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:17:43 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:18:05 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:18:05 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:18:05 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:18:08 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:04 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:04 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:04 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:08 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:17 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:17 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:17 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:20 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:25 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:25 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:25 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:28 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:34 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:34 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:34 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:37 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:39 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:39 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:39 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:42 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:48 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:48 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:48 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:51 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:19:56 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:19:56 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:19:56 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:19:59 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.
30/09/2026 00:20:01 - 0|CORE|🕵️. Buscando Modulo USB para el S.O - linux_x64_gui
30/09/2026 00:20:01 - 0|CORE|✅ Modulo node-hid cargado correctamente.
30/09/2026 00:20:01 - 0|CORE|⏳ Iniciando Ema Libre en Modo gráfico o Web.
30/09/2026 00:20:04 - 0|CORE|🔚 SALIR: Se cerro el core de EMA LIBRE. Hasta luego!!!.

View File

@ -27,6 +27,7 @@ async function probe() {
const check = (value, message) => { if (!value) throw new Error(message); }; const check = (value, message) => { if (!value) throw new Error(message); };
const tests = []; const tests = [];
try { try {
nw.Window.get().show();
await wait(200); await wait(200);
check($.fn.jquery.startsWith('3.'), 'Expected jQuery 3'); check($.fn.jquery.startsWith('3.'), 'Expected jQuery 3');
check(bootstrap.Tooltip.VERSION.startsWith('5.'), 'Expected Bootstrap 5'); check(bootstrap.Tooltip.VERSION.startsWith('5.'), 'Expected Bootstrap 5');
@ -36,6 +37,33 @@ async function probe() {
check(document.querySelector(tab.dataset.bsTarget)?.classList.contains('active'), 'Tab failed: ' + tab.id); check(document.querySelector(tab.dataset.bsTarget)?.classList.contains('active'), 'Tab failed: ' + tab.id);
} }
tests.push('all navigation tabs'); tests.push('all navigation tabs');
const home = document.getElementById('inicio');
home.innerHTML = window.homeHTML;
document.getElementById('inicio-tab').click();
const live = {state:[true,''], system:'SI', record:{fecha:'30/09/2026 12:00:00', temperatura_externa:0, humedad_externa:65, viento_velocidad:10, lluvia_acu_diaria:2.5}, publications:[]};
EstadoUI.render(live);
check(home.querySelectorAll('.ema-measurement').length === 4, 'Missing measurement cards');
const temperature = home.querySelector('[data-medicion="temperatura_externa"]');
check(temperature.textContent.includes('0°C'), 'Zero temperature treated as missing');
temperature.focus(); EstadoUI.render(live);
check(document.activeElement === temperature, 'Refresh lost focused measurement');
EstadoUI.render({...live, system:'SA'});
check(temperature.textContent.includes('32°F'), 'Home imperial conversion failed');
EstadoUI.render({...live, record:false});
check(temperature.textContent.includes('—'), 'Missing measurement shown as zero');
EstadoUI.render(live);
temperature.click();
check(document.getElementById('exterior').classList.contains('active'), 'Card detail navigation failed');
document.getElementById('inicio-tab').click();
await wait(250);
check(getComputedStyle(document.querySelector('#list-tab .nav-link.active')).backgroundColor === 'rgb(26, 96, 111)', 'Active navigation palette missing: ' + getComputedStyle(document.querySelector('#list-tab .nav-link.active')).backgroundColor);
check(getComputedStyle(document.getElementById('estado-estacion')).position === 'sticky', 'Status is not sticky');
for (const id of ['botoniniciar','botondetener','inombre','imodo','estado','iusb','mensaje','mensajedetalle','tamanio_diario','tamanio_pendiente','tamanio_debug','audiomenu','autoactivado','iso','nwjs_v','node_pid','node_ppid']) check(home.querySelector('#'+id), 'Missing existing control: '+id);
nw.Window.get().show(); await wait(350);
const homeCapture = await new Promise(resolve => nw.Window.get().capturePage(resolve, {format:'png', datatype:'buffer'}));
require('fs').writeFileSync(window.resultFile + '.inicio.png', homeCapture);
tests.push('home live cards, units, missing data, stable focus, detail links, active navigation and sticky status');
const statsRoot = document.getElementById('estadisticas'); const statsRoot = document.getElementById('estadisticas');
statsRoot.innerHTML = window.statisticsHTML; statsRoot.innerHTML = window.statisticsHTML;
const records = [ const records = [
@ -128,6 +156,10 @@ async function probe() {
language: {url: window.languageURL}, initComplete() { resolve(this.api()); }}); language: {url: window.languageURL}, initComplete() { resolve(this.api()); }});
}); });
check(table.rows().count() === 2, 'Missing table rows'); check(table.rows().count() === 2, 'Missing table rows');
check(document.querySelector('#table tbody .ema-number'), 'Numeric cells not aligned');
const selectedRow = document.querySelector('#table tbody tr'); selectedRow.click();
check(selectedRow.classList.contains('ema-selected'), 'Row selection missing');
check(!!document.querySelector('.dataTables_scrollHead'), 'Fixed table header missing');
table.search('Beta').draw(); table.search('Beta').draw();
check(table.rows({search: 'applied'}).count() === 1, 'Table search failed'); check(table.rows({search: 'applied'}).count() === 1, 'Table search failed');
check(table.buttons.exportData().body[0][0] === 'Beta', 'CSV/copy data failed'); check(table.buttons.exportData().body[0][0] === 'Beta', 'CSV/copy data failed');
@ -179,9 +211,9 @@ async function run(mode, origin) {
const fixture = require('./fixtures/configuracion.cjs')(); const fixture = require('./fixtures/configuracion.cjs')();
fs.writeFileSync(path.join(folder, 'index.html'), `<!doctype html><meta charset="utf-8"><base href="${base}"> fs.writeFileSync(path.join(folder, 'index.html'), `<!doctype html><meta charset="utf-8"><base href="${base}">
<script>window.failures=[];addEventListener('error',e=>failures.push(e.message||'Resource: '+e.target.src),true);addEventListener('unhandledrejection',e=>failures.push(String(e.reason)));</script> <script>window.failures=[];addEventListener('error',e=>failures.push(e.message||'Resource: '+e.target.src),true);addEventListener('unhandledrejection',e=>failures.push(String(e.reason)));</script>
${head}<body class="dia"><ul class="nav nav-tabs">${tabs.join('')}</ul>${panes}<section id="estado-estacion" class="estado-estacion"></section><div id="settings"></div> ${head}<body class="dia"><section id="estado-estacion" class="estado-estacion"></section><ul id="list-tab" class="nav nav-tabs nav-pills nav-fill">${tabs.join('')}</ul><div class="tab-content ema-panel">${panes}</div><div id="settings"></div>
<button id="tip" title="Prueba" class="btn btn-primary">Tooltip</button> <button id="tip" title="Prueba" class="btn btn-primary">Tooltip</button>
<table id="table" class="table table-bordered"><thead><tr><th>Nombre</th></tr></thead><tbody><tr><td>Alpha</td></tr><tr><td>Beta</td></tr></tbody></table> <table id="table" class="table table-bordered"><thead><tr><th>Nombre</th><th>Valor</th></tr></thead><tbody><tr><td>Alpha</td><td>12.5</td></tr><tr><td>Beta</td><td>0</td></tr></tbody></table>
<script> <script>
window.configFixture=${JSON.stringify(fixture)}; window.configFixture=${JSON.stringify(fixture)};
window.persistConfiguration=async draft => { window.persistConfiguration=async draft => {
@ -189,7 +221,7 @@ window.persistConfiguration=async draft => {
else {const response=await $.ajax({url:'/GuardarConf',method:'POST',contentType:'application/json',data:JSON.stringify({configuracion:draft})});if(!response.ok)throw new Error('Save failed');} else {const response=await $.ajax({url:'/GuardarConf',method:'POST',contentType:'application/json',data:JSON.stringify({configuracion:draft})});if(!response.ok)throw new Error('Save failed');}
}; };
window.readConfiguration=()=>{const sandbox={module:{exports:{}}};require('vm').runInNewContext(require('fs').readFileSync(${JSON.stringify(configFile)},'utf8'),sandbox);return sandbox.module.exports;}; window.readConfiguration=()=>{const sandbox={module:{exports:{}}};require('vm').runInNewContext(require('fs').readFileSync(${JSON.stringify(configFile)},'utf8'),sandbox);return sandbox.module.exports;};
window.statisticsHTML=${JSON.stringify(read(mode + '/view/modelo_estadisticas.html'))};window.settingsHTML=${JSON.stringify(settings)};window.languageURL=${JSON.stringify(language)};window.resultFile=${JSON.stringify(resultFile)};addEventListener('load',${probe.toString()});</script>`); window.homeHTML=${JSON.stringify(read(mode + '/view/modelo_inicio.html'))};window.statisticsHTML=${JSON.stringify(read(mode + '/view/modelo_estadisticas.html'))};window.settingsHTML=${JSON.stringify(settings)};window.languageURL=${JSON.stringify(language)};window.resultFile=${JSON.stringify(resultFile)};addEventListener('load',${probe.toString()});</script>`);
const args = JSON.parse(read('package.json'))['chromium-args'].replace('--disable-logging', ''); const args = JSON.parse(read('package.json'))['chromium-args'].replace('--disable-logging', '');
fs.writeFileSync(path.join(folder, 'package.json'), JSON.stringify({name:'carpincho-ui-test',main:'index.html','chromium-args':args,window:{show:false,width:1200,height:900}})); fs.writeFileSync(path.join(folder, 'package.json'), JSON.stringify({name:'carpincho-ui-test',main:'index.html','chromium-args':args,window:{show:false,width:1200,height:900}}));
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {