stackhelx 1.0.0__py3-none-any.whl
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.
- stackhelx/__init__.py +2 -0
- stackhelx/__main__.py +4 -0
- stackhelx/browse.py +160 -0
- stackhelx/cli.py +1165 -0
- stackhelx/config.py +448 -0
- stackhelx/detect.py +1053 -0
- stackhelx/docker.py +202 -0
- stackhelx/doctor.py +356 -0
- stackhelx/guardrails.py +42 -0
- stackhelx/history.py +86 -0
- stackhelx/mcp.py +467 -0
- stackhelx/ports.py +392 -0
- stackhelx/registry.py +356 -0
- stackhelx/runner.py +877 -0
- stackhelx/scripts.py +123 -0
- stackhelx/server.py +1786 -0
- stackhelx/tunnel.py +207 -0
- stackhelx/web/app.css +1729 -0
- stackhelx/web/app.js +2425 -0
- stackhelx/web/index.html +378 -0
- stackhelx/web/tokens.css +104 -0
- stackhelx-1.0.0.dist-info/METADATA +323 -0
- stackhelx-1.0.0.dist-info/RECORD +26 -0
- stackhelx-1.0.0.dist-info/WHEEL +4 -0
- stackhelx-1.0.0.dist-info/entry_points.txt +3 -0
- stackhelx-1.0.0.dist-info/licenses/LICENSE +21 -0
stackhelx/web/index.html
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>StackHelx</title>
|
|
7
|
+
<link rel="icon" id="favicon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='40' fill='%2364748b'/%3E%3C/svg%3E" />
|
|
8
|
+
<!-- Sin `?v=`: el servidor manda Cache-Control: no-store en toda respuesta,
|
|
9
|
+
mounts incluidos, asi que el navegador ya no cachea nada. Mantenerlo a
|
|
10
|
+
mano solo servia para olvidarse de subirlo y cargar la pagina en blanco. -->
|
|
11
|
+
<link rel="stylesheet" href="/static/app.css" />
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<header class="masthead">
|
|
15
|
+
<h1 class="wordmark">StackHelx</h1>
|
|
16
|
+
<p class="masthead__status" id="connection" role="status">Conectando…</p>
|
|
17
|
+
<p class="masthead__health" id="health" hidden></p>
|
|
18
|
+
<button class="btn btn--quiet masthead__notify" type="button" id="btn-mcp-modal" title="Telemetría y llamadas de herramientas MCP">
|
|
19
|
+
MCP
|
|
20
|
+
</button>
|
|
21
|
+
<button class="btn btn--quiet masthead__notify" type="button" id="notify" hidden>
|
|
22
|
+
Avisarme
|
|
23
|
+
</button>
|
|
24
|
+
</header>
|
|
25
|
+
|
|
26
|
+
<main class="sheet">
|
|
27
|
+
<p class="lede">
|
|
28
|
+
Tus stacks locales. Arrancá, apagá, liberá los puertos que quedaron tomados.
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<div class="find" id="find" hidden>
|
|
32
|
+
<div class="find__bar">
|
|
33
|
+
<svg class="find__icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
34
|
+
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" />
|
|
35
|
+
</svg>
|
|
36
|
+
<input
|
|
37
|
+
class="find__input"
|
|
38
|
+
id="search"
|
|
39
|
+
type="search"
|
|
40
|
+
autocomplete="off"
|
|
41
|
+
spellcheck="false"
|
|
42
|
+
placeholder="Buscar por nombre o ruta…"
|
|
43
|
+
/>
|
|
44
|
+
<kbd class="kbd" title="Presioná / para buscar">/</kbd>
|
|
45
|
+
<span class="find__count" id="count" role="status"></span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="filter-chips" id="filter-chips" role="tablist" aria-label="Filtrar por estado">
|
|
48
|
+
<button class="chip chip--active" type="button" data-status="" role="tab" aria-selected="true">
|
|
49
|
+
<span>Todos</span>
|
|
50
|
+
</button>
|
|
51
|
+
<button class="chip" type="button" data-status="running" role="tab" aria-selected="false">
|
|
52
|
+
<span class="dot" data-tone="good" aria-hidden="true"></span>
|
|
53
|
+
<span>Corriendo</span>
|
|
54
|
+
</button>
|
|
55
|
+
<button class="chip" type="button" data-status="stopped" role="tab" aria-selected="false">
|
|
56
|
+
<span class="dot" data-tone="neutral" aria-hidden="true"></span>
|
|
57
|
+
<span>Detenidos</span>
|
|
58
|
+
</button>
|
|
59
|
+
<button class="chip" type="button" data-status="error" role="tab" aria-selected="false">
|
|
60
|
+
<span class="dot" data-tone="bad" aria-hidden="true"></span>
|
|
61
|
+
<span>Con error</span>
|
|
62
|
+
</button>
|
|
63
|
+
<button class="chip chip--ports" type="button" id="btn-ports-modal">
|
|
64
|
+
<span>Mapa de puertos</span>
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div id="projects" class="index" aria-busy="true"></div>
|
|
70
|
+
|
|
71
|
+
<nav class="pager" id="pager" aria-label="Paginas de proyectos" hidden>
|
|
72
|
+
<button class="btn" type="button" data-page="prev">Anterior</button>
|
|
73
|
+
<span class="pager__at" id="pager-at"></span>
|
|
74
|
+
<button class="btn" type="button" data-page="next">Siguiente</button>
|
|
75
|
+
</nav>
|
|
76
|
+
|
|
77
|
+
<p class="empty" id="empty" hidden>
|
|
78
|
+
Todavía no registraste ningún proyecto. Corré <code>stackhelx add</code>
|
|
79
|
+
dentro de una carpeta con <code>stack.yaml</code>, o pegá la ruta acá abajo.
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
<!-- A diferencia de la de intrusos, esta si desaparece cuando esta vacia.
|
|
83
|
+
Un "no hay intrusos" informa que el chequeo corrio; un "no hay
|
|
84
|
+
tuneles" es ruido permanente para algo que solo existe si vos
|
|
85
|
+
apretaste el boton. Cuando hay, es lo que mas grita de la pagina. -->
|
|
86
|
+
<section class="orphans tunnels" id="tunnels" hidden>
|
|
87
|
+
<div class="orphans__header">
|
|
88
|
+
<div>
|
|
89
|
+
<h2 class="orphans__heading" id="tunnels-heading">Túneles abiertos</h2>
|
|
90
|
+
<p class="orphans__desc">
|
|
91
|
+
Estos puertos están accesibles desde internet por una URL pública.
|
|
92
|
+
Se cierran solos al apagar StackHelx.
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<ul class="orphans__list" id="tunnels-list"></ul>
|
|
97
|
+
</section>
|
|
98
|
+
|
|
99
|
+
<section class="orphans" id="orphans" hidden>
|
|
100
|
+
<div class="orphans__header">
|
|
101
|
+
<div>
|
|
102
|
+
<h2 class="orphans__heading" id="orphans-heading">Procesos intrusos</h2>
|
|
103
|
+
<p class="orphans__desc">Puertos de tus proyectos ocupados por procesos que no arrancó StackHelx. Podés cerrarlos desde acá.</p>
|
|
104
|
+
</div>
|
|
105
|
+
<button class="btn btn--kill-all" type="button" id="orphans-kill-all" hidden>Liberar todos</button>
|
|
106
|
+
</div>
|
|
107
|
+
<ul class="orphans__list" id="orphans-list"></ul>
|
|
108
|
+
</section>
|
|
109
|
+
|
|
110
|
+
<form class="enroll" id="enroll">
|
|
111
|
+
<label class="enroll__label" for="path">Registrar un proyecto</label>
|
|
112
|
+
<div class="enroll__row">
|
|
113
|
+
<input
|
|
114
|
+
class="enroll__input"
|
|
115
|
+
id="path"
|
|
116
|
+
name="path"
|
|
117
|
+
type="text"
|
|
118
|
+
list="path-suggestions"
|
|
119
|
+
autocomplete="off"
|
|
120
|
+
spellcheck="false"
|
|
121
|
+
placeholder="A:\Proyectos\mi-app"
|
|
122
|
+
required
|
|
123
|
+
/>
|
|
124
|
+
<datalist id="path-suggestions"></datalist>
|
|
125
|
+
<button class="btn" type="button" id="browse">Explorar…</button>
|
|
126
|
+
<button class="btn btn--primary" type="submit">Registrar</button>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="enroll__tools">
|
|
129
|
+
<a class="btn btn--quiet" id="btn-export" href="/api/projects/export" download="stackhelx-projects.json">Exportar proyectos</a>
|
|
130
|
+
<button class="btn btn--quiet" type="button" id="btn-import">Importar JSON</button>
|
|
131
|
+
<!-- Los dos se ven siempre que algun proyecto use Docker. El boton
|
|
132
|
+
abre o reinicia segun el estado; el texto lo pone updateDocker. -->
|
|
133
|
+
<span class="tools__docker" id="docker-state" hidden></span>
|
|
134
|
+
<button class="btn btn--quiet" type="button" id="btn-docker" hidden></button>
|
|
135
|
+
<button class="btn btn--quiet" type="button" id="btn-docker-clean" hidden>Limpiar Docker</button>
|
|
136
|
+
<input type="file" id="file-import" accept=".json" hidden />
|
|
137
|
+
</div>
|
|
138
|
+
</form>
|
|
139
|
+
|
|
140
|
+
<!-- Elegir que limpiar. El boton abria un prune de todo junto, y el cache
|
|
141
|
+
de build puede ser decenas de GB que se regeneran solas mientras que
|
|
142
|
+
una imagen sin tag puede ser la capa base que vas a volver a bajar.
|
|
143
|
+
No es lo mismo, y hasta ahora iba en el mismo paquete. -->
|
|
144
|
+
<dialog class="picker" id="clean-modal" aria-label="Limpiar Docker">
|
|
145
|
+
<div class="picker__head">
|
|
146
|
+
<p class="picker__path">Qué limpiar de Docker</p>
|
|
147
|
+
<button class="btn btn--quiet" type="button" data-clean="close">Cerrar</button>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="clean">
|
|
150
|
+
<pre class="clean__usage" id="clean-usage" tabindex="0"></pre>
|
|
151
|
+
<ul class="clean__list" id="clean-targets"></ul>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="picker__foot">
|
|
154
|
+
<span class="clean__warn" id="clean-warn" aria-live="polite"></span>
|
|
155
|
+
<button class="btn btn--primary" type="button" id="clean-run">Limpiar</button>
|
|
156
|
+
</div>
|
|
157
|
+
</dialog>
|
|
158
|
+
|
|
159
|
+
<dialog class="picker ports-modal" id="ports-modal" aria-label="Mapa de puertos">
|
|
160
|
+
<div class="picker__head">
|
|
161
|
+
<p class="picker__path">Mapa de puertos del sistema</p>
|
|
162
|
+
<button class="btn btn--quiet" type="button" data-ports-modal="close">Cerrar</button>
|
|
163
|
+
</div>
|
|
164
|
+
<ul class="orphans__list" id="ports-modal-list"></ul>
|
|
165
|
+
</dialog>
|
|
166
|
+
|
|
167
|
+
<dialog class="picker mcp-modal" id="mcp-modal" aria-label="Telemetría de Agentes MCP">
|
|
168
|
+
<div class="picker__head">
|
|
169
|
+
<p class="picker__path">Telemetría de Agentes MCP</p>
|
|
170
|
+
<button class="btn btn--quiet" type="button" data-mcp-modal="close">Cerrar</button>
|
|
171
|
+
</div>
|
|
172
|
+
<div class="mcp__content">
|
|
173
|
+
<div class="mcp__stats">
|
|
174
|
+
<span class="mcp__stat">Llamadas totales: <strong id="mcp-total-calls">0</strong></span>
|
|
175
|
+
<span class="mcp__stat">Cuota activa: <strong id="mcp-quota-used">0/30</strong> req/min</span>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="mcp__breakdown" id="mcp-breakdown"></div>
|
|
178
|
+
<div class="mcp__table-scroll">
|
|
179
|
+
<table class="history__table mcp__table">
|
|
180
|
+
<thead>
|
|
181
|
+
<tr>
|
|
182
|
+
<th>Hora</th>
|
|
183
|
+
<th>Herramienta</th>
|
|
184
|
+
<th>Duración</th>
|
|
185
|
+
<th>Estado</th>
|
|
186
|
+
</tr>
|
|
187
|
+
</thead>
|
|
188
|
+
<tbody id="mcp-tbody"></tbody>
|
|
189
|
+
</table>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</dialog>
|
|
193
|
+
|
|
194
|
+
<dialog class="picker" id="picker" aria-label="Elegir carpeta">
|
|
195
|
+
<div class="picker__head">
|
|
196
|
+
<p class="picker__path" id="picker-path"></p>
|
|
197
|
+
<button class="btn btn--quiet" type="button" data-picker="close">Cerrar</button>
|
|
198
|
+
</div>
|
|
199
|
+
<div class="picker__frequent" id="picker-frequent" hidden>
|
|
200
|
+
<span class="picker__frequent-label">Frecuentes:</span>
|
|
201
|
+
<div class="picker__frequent-chips" id="picker-frequent-chips"></div>
|
|
202
|
+
</div>
|
|
203
|
+
<ul class="picker__list" id="picker-list"></ul>
|
|
204
|
+
<p class="picker__note" id="picker-note" hidden></p>
|
|
205
|
+
<div class="picker__foot">
|
|
206
|
+
<div class="picker__nav">
|
|
207
|
+
<button class="btn" type="button" data-picker="back" disabled>Volver</button>
|
|
208
|
+
<button class="btn" type="button" data-picker="up">Arriba</button>
|
|
209
|
+
<button class="btn btn--quiet" type="button" data-picker="os-folder" title="Abrir esta carpeta en el explorador de archivos del sistema">Abrir en Explorador</button>
|
|
210
|
+
</div>
|
|
211
|
+
<button class="btn btn--primary" type="button" data-picker="pick">Usar esta carpeta</button>
|
|
212
|
+
</div>
|
|
213
|
+
</dialog>
|
|
214
|
+
|
|
215
|
+
<dialog class="picker" id="auth-modal" aria-label="Autenticación">
|
|
216
|
+
<div class="picker__head">
|
|
217
|
+
<p class="picker__path">Token de StackHelx requerido</p>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="auth">
|
|
220
|
+
<p class="auth__hint">
|
|
221
|
+
Pegá tu token de acceso (lo encontrás en <code>~/.stackhelx/token</code>).
|
|
222
|
+
</p>
|
|
223
|
+
<input
|
|
224
|
+
class="enroll__input auth__input"
|
|
225
|
+
id="auth-token-input"
|
|
226
|
+
type="password"
|
|
227
|
+
placeholder="Pegá el token acá..."
|
|
228
|
+
/>
|
|
229
|
+
</div>
|
|
230
|
+
<div class="picker__foot">
|
|
231
|
+
<button class="btn btn--primary" type="button" id="auth-token-save">Guardar token</button>
|
|
232
|
+
</div>
|
|
233
|
+
</dialog>
|
|
234
|
+
</main>
|
|
235
|
+
|
|
236
|
+
<footer class="colophon">
|
|
237
|
+
<!-- Decia "Nada de esto sale de tu máquina", escrito antes de que
|
|
238
|
+
existiera el botón Túnel, que está tres bloques más arriba en esta
|
|
239
|
+
misma página. La primera mitad sigue siendo cierta y es la que
|
|
240
|
+
tranquiliza; la segunda avisa donde corresponde. -->
|
|
241
|
+
<span>Solo loopback. Un túnel que abras acá sí expone ese puerto a internet.</span>
|
|
242
|
+
<!-- Sin esto no hay forma de saber si el navegador esta sirviendo una
|
|
243
|
+
pagina vieja de su cache. Lo llena el JS con lo que contesta el
|
|
244
|
+
servidor: si no aparece, lo cargado es viejo. -->
|
|
245
|
+
<span class="colophon__build" id="build" title="Version que sirve el servidor"></span>
|
|
246
|
+
</footer>
|
|
247
|
+
|
|
248
|
+
<p class="flash" id="flash" role="alert" hidden></p>
|
|
249
|
+
|
|
250
|
+
<template id="tpl-project">
|
|
251
|
+
<article class="project" data-expanded="false">
|
|
252
|
+
<div class="project__head">
|
|
253
|
+
<div class="project__title">
|
|
254
|
+
<h2 class="project__name"></h2>
|
|
255
|
+
<p class="state"><span class="dot"></span><span class="state__text"></span></p>
|
|
256
|
+
</div>
|
|
257
|
+
<div class="project__head-actions">
|
|
258
|
+
<a class="btn btn--open project__open" target="_blank" rel="noopener noreferrer" hidden>
|
|
259
|
+
Abrir ↗
|
|
260
|
+
</a>
|
|
261
|
+
<button class="btn btn--primary" type="button" data-act="up">Arrancar</button>
|
|
262
|
+
<button class="btn" type="button" data-act="down">Apagar</button>
|
|
263
|
+
<button class="btn btn--quiet project__toggle" type="button" aria-expanded="false" aria-label="Detalles">
|
|
264
|
+
<span class="project__toggle-text">Detalles</span>
|
|
265
|
+
<span class="chevron" aria-hidden="true">▼</span>
|
|
266
|
+
</button>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
|
|
270
|
+
<div class="project__details">
|
|
271
|
+
<div class="project__body">
|
|
272
|
+
<div class="project__path-wrapper">
|
|
273
|
+
<p class="project__path"></p>
|
|
274
|
+
<div class="project__path-actions">
|
|
275
|
+
<button class="btn btn--quiet btn--sm project__path-copy" type="button" aria-label="Copiar ruta">Copiar ruta</button>
|
|
276
|
+
<button class="btn btn--quiet btn--sm" type="button" data-act="open-folder" title="Abrir en el Explorador de archivos del sistema">Carpeta</button>
|
|
277
|
+
<div class="editor-select-wrap">
|
|
278
|
+
<select class="editor__select" data-act="select-editor" title="Elegir y abrir en editor">
|
|
279
|
+
<option value="" disabled selected>Editor…</option>
|
|
280
|
+
</select>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
<p class="project__error" hidden></p>
|
|
285
|
+
<p class="project__docker-warning" hidden></p>
|
|
286
|
+
<ul class="services"></ul>
|
|
287
|
+
<div class="project__actions">
|
|
288
|
+
<label class="profile">
|
|
289
|
+
<span class="profile__label">Perfil</span>
|
|
290
|
+
<select class="profile__select"></select>
|
|
291
|
+
</label>
|
|
292
|
+
<button class="btn btn--quiet" type="button" data-act="logs" aria-expanded="false">
|
|
293
|
+
Logs
|
|
294
|
+
</button>
|
|
295
|
+
<button class="btn btn--quiet" type="button" data-act="history" aria-expanded="false">
|
|
296
|
+
Historial
|
|
297
|
+
</button>
|
|
298
|
+
<button class="btn btn--quiet" type="button" data-act="graph" aria-expanded="false" hidden>
|
|
299
|
+
Grafo
|
|
300
|
+
</button>
|
|
301
|
+
<button class="btn btn--quiet" type="button" data-act="env-audit" aria-expanded="false">
|
|
302
|
+
.env
|
|
303
|
+
</button>
|
|
304
|
+
<button class="btn btn--quiet project__freeze" type="button" data-act="freeze" hidden>
|
|
305
|
+
Congelar a stack.yaml
|
|
306
|
+
</button>
|
|
307
|
+
<button class="btn btn--quiet project__drop" type="button" data-act="drop">
|
|
308
|
+
Quitar proyecto
|
|
309
|
+
</button>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="logs__box" hidden>
|
|
312
|
+
<div class="logs__toolbar">
|
|
313
|
+
<input class="logs__filter" type="search" placeholder="Filtrar logs..." aria-label="Filtrar logs" />
|
|
314
|
+
<span class="logs__live" hidden title="Transmisión en tiempo real activa">En vivo</span>
|
|
315
|
+
<button class="btn btn--quiet btn--sm" type="button" data-act="copy-logs">Copiar</button>
|
|
316
|
+
<button class="btn btn--quiet btn--sm" type="button" data-act="clear-logs">Limpiar</button>
|
|
317
|
+
</div>
|
|
318
|
+
<pre class="logs" tabindex="0"></pre>
|
|
319
|
+
</div>
|
|
320
|
+
<div class="history__box" hidden>
|
|
321
|
+
<table class="history__table">
|
|
322
|
+
<thead>
|
|
323
|
+
<tr>
|
|
324
|
+
<th>Fecha</th>
|
|
325
|
+
<th>Perfil</th>
|
|
326
|
+
<th>Duración</th>
|
|
327
|
+
<th>Resultado</th>
|
|
328
|
+
</tr>
|
|
329
|
+
</thead>
|
|
330
|
+
<tbody class="history__tbody"></tbody>
|
|
331
|
+
</table>
|
|
332
|
+
</div>
|
|
333
|
+
<div class="graph__box" hidden>
|
|
334
|
+
<div class="graph__header">
|
|
335
|
+
<span class="graph__title">Orden de arranque y dependencias</span>
|
|
336
|
+
</div>
|
|
337
|
+
<div class="graph__canvas-wrapper">
|
|
338
|
+
<svg class="graph__svg" role="img" aria-label="Grafo de dependencias"></svg>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
<div class="env__box" hidden>
|
|
342
|
+
<div class="env__header">
|
|
343
|
+
<span class="env__status-badge"></span>
|
|
344
|
+
<span class="env__summary"></span>
|
|
345
|
+
</div>
|
|
346
|
+
<div class="env__details">
|
|
347
|
+
<div class="env__section env__section--missing" hidden>
|
|
348
|
+
<span class="env__section-title">Claves faltantes en .env:</span>
|
|
349
|
+
<ul class="env__list env__list--missing"></ul>
|
|
350
|
+
</div>
|
|
351
|
+
<div class="env__section env__section--placeholders" hidden>
|
|
352
|
+
<span class="env__section-title">Claves con placeholders inseguros:</span>
|
|
353
|
+
<ul class="env__list env__list--placeholders"></ul>
|
|
354
|
+
</div>
|
|
355
|
+
<div class="env__section env__section--empty" hidden>
|
|
356
|
+
<span class="env__section-title">Claves declaradas vacías:</span>
|
|
357
|
+
<ul class="env__list env__list--empty"></ul>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</article>
|
|
364
|
+
</template>
|
|
365
|
+
|
|
366
|
+
<template id="tpl-service">
|
|
367
|
+
<li class="service">
|
|
368
|
+
<span class="service__name"><span class="service__label"></span></span>
|
|
369
|
+
<span class="service__port"></span>
|
|
370
|
+
<span class="service__state"><span class="dot"></span><span></span></span>
|
|
371
|
+
<span class="service__metrics" hidden></span>
|
|
372
|
+
<span class="service__act"></span>
|
|
373
|
+
</li>
|
|
374
|
+
</template>
|
|
375
|
+
|
|
376
|
+
<script src="/static/app.js"></script>
|
|
377
|
+
</body>
|
|
378
|
+
</html>
|
stackhelx/web/tokens.css
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* Hallmark · theme: custom "Ledger"
|
|
2
|
+
* axes: light paper / roman-serif display / warm accent
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
:root {
|
|
6
|
+
/* Los controles nativos (select, input, scrollbar) siguen al tema del SO en
|
|
7
|
+
vez de quedar blancos sobre papel oscuro. */
|
|
8
|
+
color-scheme: light dark;
|
|
9
|
+
|
|
10
|
+
/* papel y tinta. Nunca #fff ni #000: los dos son mas duros que cualquier
|
|
11
|
+
papel real y delatan una paleta elegida sin mirar. */
|
|
12
|
+
--color-paper: oklch(96.5% 0.008 85);
|
|
13
|
+
--color-paper-2: oklch(98.4% 0.005 85);
|
|
14
|
+
--color-paper-3: oklch(93.2% 0.011 85);
|
|
15
|
+
--color-ink: oklch(23% 0.012 60);
|
|
16
|
+
--color-ink-2: oklch(47% 0.011 60);
|
|
17
|
+
/* ink-3 llega justo a 4.5:1 sobre el papel. Es el piso, no una eleccion
|
|
18
|
+
estetica: por debajo de 54% de luminosidad el texto chico deja de leerse. */
|
|
19
|
+
--color-ink-3: oklch(54% 0.010 60);
|
|
20
|
+
--color-rule: oklch(89% 0.009 82);
|
|
21
|
+
--color-rule-strong: oklch(82% 0.011 82);
|
|
22
|
+
|
|
23
|
+
/* un solo acento, arcilla, usado en menos del 5% de la pantalla */
|
|
24
|
+
--color-accent: oklch(52% 0.13 42);
|
|
25
|
+
--color-accent-soft: oklch(92% 0.035 42);
|
|
26
|
+
|
|
27
|
+
/* estados. Verde apagado y no neon: esto es un panel, no un semaforo.
|
|
28
|
+
Los tres pasan 3:1 sobre el papel, que es lo que pide un indicador. */
|
|
29
|
+
--color-ok: oklch(52% 0.095 155);
|
|
30
|
+
--color-warn: oklch(62% 0.115 75);
|
|
31
|
+
--color-bad: oklch(51% 0.155 28);
|
|
32
|
+
--color-bad-soft: oklch(94% 0.028 28);
|
|
33
|
+
/* Tinta de las pastillas de estado. El color base esta calibrado para pintar
|
|
34
|
+
un punto sobre el papel, y sobre su propia pastilla al 14% no llega a 4.5:1
|
|
35
|
+
(medido: ok 3.97, warn 2.90, bad 4.56). Estas tres bajan la luminosidad lo
|
|
36
|
+
justo. En oscuro no hacen falta: ahi el base ya da entre 4.9 y 6.9. */
|
|
37
|
+
--color-ok-ink: oklch(46% 0.095 155);
|
|
38
|
+
--color-warn-ink: oklch(50% 0.115 75);
|
|
39
|
+
--color-bad-ink: oklch(48% 0.155 28);
|
|
40
|
+
--color-bad-rule: oklch(78% 0.07 28);
|
|
41
|
+
|
|
42
|
+
/* tipografia: tres roles, cero descargas. Ver la nota en app.css. */
|
|
43
|
+
--font-display: ui-serif, Georgia, "Times New Roman", serif;
|
|
44
|
+
--font-body:
|
|
45
|
+
system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
|
|
46
|
+
--font-mono:
|
|
47
|
+
ui-monospace, "Cascadia Mono", "SF Mono", "JetBrains Mono", Consolas,
|
|
48
|
+
monospace;
|
|
49
|
+
|
|
50
|
+
--text-xs: 0.8125rem;
|
|
51
|
+
--text-sm: 0.9375rem;
|
|
52
|
+
--text-base: 1rem;
|
|
53
|
+
--text-lg: 1.5rem;
|
|
54
|
+
--text-xl: 1.875rem;
|
|
55
|
+
|
|
56
|
+
/* escala de 4pt */
|
|
57
|
+
--space-xs: 0.25rem;
|
|
58
|
+
--space-sm: 0.5rem;
|
|
59
|
+
--space-md: 1rem;
|
|
60
|
+
--space-lg: 1.5rem;
|
|
61
|
+
--space-xl: 2.5rem;
|
|
62
|
+
--space-2xl: 4rem;
|
|
63
|
+
--space-gutter: 1.5rem;
|
|
64
|
+
|
|
65
|
+
--rule: 1px;
|
|
66
|
+
--radius: 3px;
|
|
67
|
+
|
|
68
|
+
--dur-fast: 120ms;
|
|
69
|
+
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@media (min-width: 48rem) {
|
|
73
|
+
:root {
|
|
74
|
+
--space-gutter: 2.5rem;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Modo oscuro: la herramienta corre al lado del editor y tiene que seguir al
|
|
79
|
+
sistema. Mismo papel calido, invertido; el acento sube en luminosidad para
|
|
80
|
+
mantener contraste sobre fondo oscuro. */
|
|
81
|
+
@media (prefers-color-scheme: dark) {
|
|
82
|
+
:root {
|
|
83
|
+
--color-paper: oklch(20% 0.008 70);
|
|
84
|
+
--color-paper-2: oklch(24% 0.009 70);
|
|
85
|
+
--color-paper-3: oklch(28% 0.010 70);
|
|
86
|
+
--color-ink: oklch(92% 0.008 85);
|
|
87
|
+
--color-ink-2: oklch(74% 0.009 85);
|
|
88
|
+
--color-ink-3: oklch(63% 0.009 85);
|
|
89
|
+
--color-rule: oklch(31% 0.009 70);
|
|
90
|
+
--color-rule-strong: oklch(39% 0.010 70);
|
|
91
|
+
|
|
92
|
+
--color-accent: oklch(72% 0.12 45);
|
|
93
|
+
--color-accent-soft: oklch(34% 0.05 45);
|
|
94
|
+
|
|
95
|
+
--color-ok: oklch(70% 0.11 155);
|
|
96
|
+
--color-warn: oklch(78% 0.12 78);
|
|
97
|
+
--color-bad: oklch(68% 0.15 28);
|
|
98
|
+
--color-bad-soft: oklch(30% 0.05 28);
|
|
99
|
+
--color-ok-ink: var(--color-ok);
|
|
100
|
+
--color-warn-ink: var(--color-warn);
|
|
101
|
+
--color-bad-ink: var(--color-bad);
|
|
102
|
+
--color-bad-rule: oklch(44% 0.09 28);
|
|
103
|
+
}
|
|
104
|
+
}
|