@kwirthmagnify/kwirth-plugin-status 0.2.3 → 0.2.5

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/front.js +41 -5
  2. package/package.json +1 -1
package/front.js CHANGED
@@ -172,8 +172,13 @@
172
172
  return filas();
173
173
  }
174
174
  };
175
- var StatusDiagram = ({ inventory, active }) => {
175
+ var VELOCIDAD_INICIAL = 20;
176
+ var StatusDiagram = ({ inventory, active, autoRefresh }) => {
176
177
  const theme = (0, import_material2.useTheme)();
178
+ const vueltas = import_react2.default.useRef({ inventario: inventory, n: 0 });
179
+ if (vueltas.current.inventario !== inventory) vueltas.current = { inventario: inventory, n: vueltas.current.n + 1 };
180
+ const frenada = `statusFrenada${vueltas.current.n % 2}`;
181
+ const nodosPintados = import_react2.default.useRef(/* @__PURE__ */ new Map());
177
182
  const [posiciones, setPosiciones] = import_react2.default.useState(void 0);
178
183
  const [seleccionado, setSeleccionado] = import_react2.default.useState(void 0);
179
184
  const colores = {
@@ -201,7 +206,12 @@
201
206
  ...productores.map((p) => ({
202
207
  id: p.id,
203
208
  position: { x: 0, y: 0 },
204
- data: { label: p.displayName, esProductor: true, health: p.health, subscribers: p.subscribers, known: p.knownConsumers },
209
+ /*
210
+ SOLO lo que se pinta (o lo que usa el layout). Aquí llegaron a ir los suscriptores y la
211
+ salud sin que nadie los leyera, y como el nodo se rehace cuando cambia su data (ver
212
+ 'colocados'), un contador que variaba entre fotos hacía parpadear nodos idénticos.
213
+ */
214
+ data: { label: p.displayName, esProductor: true },
205
215
  // Con el grafo en vertical, la arista tiene que salir por ABAJO y entrar por ARRIBA; si
206
216
  // no, React Flow las saca por los lados y los cables dan un rodeo absurdo.
207
217
  sourcePosition: import_react3.Position.Bottom,
@@ -286,7 +296,15 @@
286
296
  if (!posiciones) {
287
297
  return /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { sx: { p: 3 } }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "body2", color: "text.secondary" }, "Laying out the graph\u2026"));
288
298
  }
289
- const colocados = nodos.map((n) => ({ ...n, position: posiciones[n.id] ?? { x: 0, y: 0 } }));
299
+ const colocados = nodos.map((n) => {
300
+ const position = posiciones[n.id] ?? { x: 0, y: 0 };
301
+ const firma = JSON.stringify([n.data, n.style, position]);
302
+ const previo = nodosPintados.current.get(n.id);
303
+ if (previo?.firma === firma) return previo.nodo;
304
+ const nodo = { ...n, position };
305
+ nodosPintados.current.set(n.id, { firma, nodo });
306
+ return nodo;
307
+ });
290
308
  return /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { sx: { height: "100%", display: "flex", flexDirection: "column", minHeight: 0 } }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Stack, { direction: "row", spacing: 1, alignItems: "center", sx: { mb: 1 } }, /* @__PURE__ */ import_react2.default.createElement(import_material2.Typography, { variant: "caption", color: "text.secondary" }, "A line means ", /* @__PURE__ */ import_react2.default.createElement("b", null, "an active subscription"), ". A ", /* @__PURE__ */ import_react2.default.createElement("b", null, "moving line"), " means its producer is delivering right now \u2014 but not how much goes to each consumer: that is measured per component, not per line.", " ", "Click a node to highlight what it is connected to; click the background to clear.")), (anonimos > 0 || canalesSueltos > 0) && /* @__PURE__ */ import_react2.default.createElement(import_material2.Stack, { direction: "row", spacing: 1, sx: { mb: 1 } }, anonimos > 0 && /* @__PURE__ */ import_react2.default.createElement(
291
309
  import_material2.Chip,
292
310
  {
@@ -309,7 +327,25 @@
309
327
  },
310
328
  "& .react-flow__controls-button:hover": { background: theme.palette.action.hover },
311
329
  "& .react-flow__controls-button svg": { fill: theme.palette.text.primary },
312
- "& .react-flow__attribution": { display: "none" }
330
+ "& .react-flow__attribution": { display: "none" },
331
+ /*
332
+ Con auto-refresco, la línea viva arranca a la velocidad de serie y va FRENANDO hasta
333
+ pararse justo cuando llega la siguiente foto: lo que se ha visto moverse es lo que
334
+ pasó en ese intervalo, y la línea no sigue diciendo "ahora" cuando el dato ya es viejo.
335
+ En manual no hay intervalo que agotar, así que se queda la animación continua de serie.
336
+
337
+ El recorrido sale de la curva: con ease-out cuadrática la velocidad inicial es 2·D/T,
338
+ así que D = VELOCIDAD_INICIAL·T/2 arranca igual que la de serie, sin tirón.
339
+ */
340
+ ...autoRefresh > 0 ? {
341
+ [`@keyframes ${frenada}`]: {
342
+ from: { strokeDashoffset: VELOCIDAD_INICIAL * autoRefresh / 2 },
343
+ to: { strokeDashoffset: 0 }
344
+ },
345
+ "& .react-flow__edge.animated path": {
346
+ animation: `${frenada} ${autoRefresh}s cubic-bezier(0.5, 1, 0.89, 1) 1 forwards`
347
+ }
348
+ } : {}
313
349
  } }, /* @__PURE__ */ import_react2.default.createElement(
314
350
  import_react3.ReactFlow,
315
351
  {
@@ -480,7 +516,7 @@
480
516
  /* @__PURE__ */ import_react4.default.createElement(import_material3.MenuItem, { value: 15 }, "Every 15s"),
481
517
  /* @__PURE__ */ import_react4.default.createElement(import_material3.MenuItem, { value: 30 }, "Every 30s"),
482
518
  /* @__PURE__ */ import_react4.default.createElement(import_material3.MenuItem, { value: 60 }, "Every minute")
483
- ), /* @__PURE__ */ import_react4.default.createElement(import_material3.Tooltip, { title: "Take a new snapshot" }, /* @__PURE__ */ import_react4.default.createElement(import_material3.IconButton, { size: "small", onClick: refresh }, /* @__PURE__ */ import_react4.default.createElement(import_icons.Refresh, { fontSize: "small" })))), /* @__PURE__ */ import_react4.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary", sx: { mb: 1 } }, "Snapshot taken at ", new Date(inventory.takenAt).toLocaleTimeString(), data.autoRefresh ? ` \u2014 refreshing every ${data.autoRefresh}s while this tab is open` : " \u2014 it does not refresh on its own", ".", " ", "Delivered counts since each component started; the rate is measured against your previous snapshot."), /* @__PURE__ */ import_react4.default.createElement(import_material3.Box, { ref: boxRef, sx: { display: "flex", flexDirection: "column", overflowY: vista === "table" ? "auto" : "hidden", overflowX: "hidden", width: "100%", flexGrow: 1, height: `calc(100vh - ${boxTop}px - 35px)` } }, vista === "graph" && /* @__PURE__ */ import_react4.default.createElement(StatusDiagram, { inventory, active: activos }), vista === "table" && /* @__PURE__ */ import_react4.default.createElement(import_material3.Table, { size: "small", stickyHeader: true }, /* @__PURE__ */ import_react4.default.createElement(import_material3.TableHead, null, /* @__PURE__ */ import_react4.default.createElement(import_material3.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "Kind"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "Name"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "State"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, { align: "right" }, "Consumers"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, { align: "right" }, "Delivered"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "Why"))), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableBody, null, componentes.map(fila)))), data.signals.length > 0 && /* @__PURE__ */ import_react4.default.createElement(import_material3.Box, { sx: { mt: 1 } }, data.signals.map((s, i) => /* @__PURE__ */ import_react4.default.createElement(import_material3.Typography, { key: i, variant: "caption", color: "error", display: "block" }, s))));
519
+ ), /* @__PURE__ */ import_react4.default.createElement(import_material3.Tooltip, { title: "Take a new snapshot" }, /* @__PURE__ */ import_react4.default.createElement(import_material3.IconButton, { size: "small", onClick: refresh }, /* @__PURE__ */ import_react4.default.createElement(import_icons.Refresh, { fontSize: "small" })))), /* @__PURE__ */ import_react4.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary", sx: { mb: 1 } }, "Snapshot taken at ", new Date(inventory.takenAt).toLocaleTimeString(), data.autoRefresh ? ` \u2014 refreshing every ${data.autoRefresh}s while this tab is open` : " \u2014 it does not refresh on its own", ".", " ", "Delivered counts since each component started; the rate is measured against your previous snapshot."), /* @__PURE__ */ import_react4.default.createElement(import_material3.Box, { ref: boxRef, sx: { display: "flex", flexDirection: "column", overflowY: vista === "table" ? "auto" : "hidden", overflowX: "hidden", width: "100%", flexGrow: 1, height: `calc(100vh - ${boxTop}px - 35px)` } }, vista === "graph" && /* @__PURE__ */ import_react4.default.createElement(StatusDiagram, { inventory, active: activos, autoRefresh: data.autoRefresh }), vista === "table" && /* @__PURE__ */ import_react4.default.createElement(import_material3.Table, { size: "small", stickyHeader: true }, /* @__PURE__ */ import_react4.default.createElement(import_material3.TableHead, null, /* @__PURE__ */ import_react4.default.createElement(import_material3.TableRow, null, /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "Kind"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "Name"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "State"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, { align: "right" }, "Consumers"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, { align: "right" }, "Delivered"), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableCell, null, "Why"))), /* @__PURE__ */ import_react4.default.createElement(import_material3.TableBody, null, componentes.map(fila)))), data.signals.length > 0 && /* @__PURE__ */ import_react4.default.createElement(import_material3.Box, { sx: { mt: 1 } }, data.signals.map((s, i) => /* @__PURE__ */ import_react4.default.createElement(import_material3.Typography, { key: i, variant: "caption", color: "error", display: "block" }, s))));
484
520
  };
485
521
 
486
522
  // src/front/StatusChannel.ts
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "id": "status",
5
5
  "name": "@kwirthmagnify/kwirth-plugin-status",
6
6
  "displayName": "Kwirth Status",
7
- "version": "0.2.3",
7
+ "version": "0.2.5",
8
8
  "description": "A look inside Kwirth: what is installed, how it is doing and who consumes what",
9
9
  "icon": "<svg viewBox='0 0 24 24'><g fill='none' stroke='currentColor' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><rect x='2.6' y='4.6' width='18.8' height='14.8' rx='2.2'/><path d='M6 12h2.6l1.5-3.4 2.6 6.8 1.5-3.4H18'/></g></svg>",
10
10
  "requiresRestart": false,