@kwirthmagnify/kwirth-plugin-status 0.2.2 → 0.2.4

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 +31 -8
  2. package/package.json +1 -1
package/front.js CHANGED
@@ -97,6 +97,10 @@
97
97
  var StatusIcon = (props) => /* @__PURE__ */ import_react.default.createElement(import_material.SvgIcon, { ...props, viewBox: "0 0 24 24" }, /* @__PURE__ */ import_react.default.createElement("g", { fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("rect", { x: "2.6", y: "4.6", width: "18.8", height: "14.8", rx: "2.2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6 12h2.6l1.5-3.4 2.6 6.8 1.5-3.4H18" })));
98
98
 
99
99
  // src/front/StatusData.ts
100
+ var countUnbrokeredConsumers = (components) => components.reduce((n, c) => {
101
+ if (c.subscribers === void 0 || c.knownConsumers === void 0) return n;
102
+ return n + Math.max(0, c.subscribers - c.knownConsumers);
103
+ }, 0);
100
104
  var StatusData = class {
101
105
  constructor() {
102
106
  this.view = "table";
@@ -168,8 +172,12 @@
168
172
  return filas();
169
173
  }
170
174
  };
171
- var StatusDiagram = ({ inventory, active }) => {
175
+ var VELOCIDAD_INICIAL = 20;
176
+ var StatusDiagram = ({ inventory, active, autoRefresh }) => {
172
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}`;
173
181
  const [posiciones, setPosiciones] = import_react2.default.useState(void 0);
174
182
  const [seleccionado, setSeleccionado] = import_react2.default.useState(void 0);
175
183
  const colores = {
@@ -275,17 +283,14 @@
275
283
  vigente = false;
276
284
  };
277
285
  }, [firmaGrafo]);
286
+ const anonimos = countUnbrokeredConsumers(inventory.components);
278
287
  if (inventory.edges.length === 0) {
279
- 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" }, "Nothing is subscribed to anything right now, so there is no graph to draw."));
288
+ 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" }, anonimos > 0 ? `There is consumption right now \u2014 ${anonimos} subscriber${anonimos > 1 ? "s" : ""} \u2014 but no graph to draw: they subscribed straight to the provider instead of going through the core, so nobody knows who they are. The table still shows how much each producer is delivering.` : "Nothing is subscribed to anything right now, so there is no graph to draw."));
280
289
  }
281
290
  if (!posiciones) {
282
291
  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"));
283
292
  }
284
293
  const colocados = nodos.map((n) => ({ ...n, position: posiciones[n.id] ?? { x: 0, y: 0 } }));
285
- const anonimos = inventory.components.reduce((n, c) => {
286
- if (c.subscribers === void 0 || c.knownConsumers === void 0) return n;
287
- return n + Math.max(0, c.subscribers - c.knownConsumers);
288
- }, 0);
289
294
  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(
290
295
  import_material2.Chip,
291
296
  {
@@ -308,7 +313,25 @@
308
313
  },
309
314
  "& .react-flow__controls-button:hover": { background: theme.palette.action.hover },
310
315
  "& .react-flow__controls-button svg": { fill: theme.palette.text.primary },
311
- "& .react-flow__attribution": { display: "none" }
316
+ "& .react-flow__attribution": { display: "none" },
317
+ /*
318
+ Con auto-refresco, la línea viva arranca a la velocidad de serie y va FRENANDO hasta
319
+ pararse justo cuando llega la siguiente foto: lo que se ha visto moverse es lo que
320
+ pasó en ese intervalo, y la línea no sigue diciendo "ahora" cuando el dato ya es viejo.
321
+ En manual no hay intervalo que agotar, así que se queda la animación continua de serie.
322
+
323
+ El recorrido sale de la curva: con ease-out cuadrática la velocidad inicial es 2·D/T,
324
+ así que D = VELOCIDAD_INICIAL·T/2 arranca igual que la de serie, sin tirón.
325
+ */
326
+ ...autoRefresh > 0 ? {
327
+ [`@keyframes ${frenada}`]: {
328
+ from: { strokeDashoffset: VELOCIDAD_INICIAL * autoRefresh / 2 },
329
+ to: { strokeDashoffset: 0 }
330
+ },
331
+ "& .react-flow__edge.animated path": {
332
+ animation: `${frenada} ${autoRefresh}s cubic-bezier(0.5, 1, 0.89, 1) 1 forwards`
333
+ }
334
+ } : {}
312
335
  } }, /* @__PURE__ */ import_react2.default.createElement(
313
336
  import_react3.ReactFlow,
314
337
  {
@@ -479,7 +502,7 @@
479
502
  /* @__PURE__ */ import_react4.default.createElement(import_material3.MenuItem, { value: 15 }, "Every 15s"),
480
503
  /* @__PURE__ */ import_react4.default.createElement(import_material3.MenuItem, { value: 30 }, "Every 30s"),
481
504
  /* @__PURE__ */ import_react4.default.createElement(import_material3.MenuItem, { value: 60 }, "Every minute")
482
- ), /* @__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))));
505
+ ), /* @__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))));
483
506
  };
484
507
 
485
508
  // 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.2",
7
+ "version": "0.2.4",
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,