@kwirthmagnify/kwirth-plugin-provider-debug 0.1.0 → 0.1.2
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.
- package/front.js +159 -11
- package/package.json +1 -1
package/front.js
CHANGED
|
@@ -99,6 +99,8 @@
|
|
|
99
99
|
this.events = [];
|
|
100
100
|
this.providers = [];
|
|
101
101
|
this.signals = [];
|
|
102
|
+
this.configAccepted = false;
|
|
103
|
+
this.subscribed = false;
|
|
102
104
|
this.paused = false;
|
|
103
105
|
this.started = false;
|
|
104
106
|
}
|
|
@@ -232,6 +234,27 @@
|
|
|
232
234
|
if (token === "true" || token === "false" || token === "null") return theme.palette.secondary.main;
|
|
233
235
|
return theme.palette.warning.main;
|
|
234
236
|
};
|
|
237
|
+
const paint = (text, color, keyBase) => {
|
|
238
|
+
const needle = (props.highlight ?? "").trim();
|
|
239
|
+
if (needle === "") return [/* @__PURE__ */ import_react2.default.createElement("span", { key: keyBase, style: color ? { color } : void 0 }, text)];
|
|
240
|
+
const nodes = [];
|
|
241
|
+
const lower = text.toLowerCase();
|
|
242
|
+
const target = needle.toLowerCase();
|
|
243
|
+
let from = 0;
|
|
244
|
+
let at = lower.indexOf(target);
|
|
245
|
+
while (at >= 0) {
|
|
246
|
+
if (at > from) nodes.push(/* @__PURE__ */ import_react2.default.createElement("span", { key: `${keyBase}-${from}`, style: color ? { color } : void 0 }, text.slice(from, at)));
|
|
247
|
+
nodes.push(
|
|
248
|
+
// el marcador permite que el buscador centre la COINCIDENCIA y no la tarjeta: con un
|
|
249
|
+
// JSON de miles de líneas, centrar la tarjeta deja el resultado fuera de pantalla
|
|
250
|
+
/* @__PURE__ */ import_react2.default.createElement("span", { key: `${keyBase}-h${at}`, "data-pd-hit": "1", style: { backgroundColor: theme.palette.text.primary, color: theme.palette.background.paper } }, text.slice(at, at + needle.length))
|
|
251
|
+
);
|
|
252
|
+
from = at + needle.length;
|
|
253
|
+
at = lower.indexOf(target, from);
|
|
254
|
+
}
|
|
255
|
+
if (from < text.length) nodes.push(/* @__PURE__ */ import_react2.default.createElement("span", { key: `${keyBase}-${from}`, style: color ? { color } : void 0 }, text.slice(from)));
|
|
256
|
+
return nodes;
|
|
257
|
+
};
|
|
235
258
|
const render = () => {
|
|
236
259
|
let text;
|
|
237
260
|
try {
|
|
@@ -244,11 +267,11 @@
|
|
|
244
267
|
let match;
|
|
245
268
|
TOKEN.lastIndex = 0;
|
|
246
269
|
while ((match = TOKEN.exec(text)) !== null) {
|
|
247
|
-
if (match.index > last) nodes.push(
|
|
248
|
-
nodes.push(
|
|
270
|
+
if (match.index > last) nodes.push(...paint(text.slice(last, match.index), void 0, `p${last}`));
|
|
271
|
+
nodes.push(...paint(match[0], colorOf(match[0]), `t${match.index}`));
|
|
249
272
|
last = match.index + match[0].length;
|
|
250
273
|
}
|
|
251
|
-
if (last < text.length) nodes.push(
|
|
274
|
+
if (last < text.length) nodes.push(...paint(text.slice(last), void 0, `p${last}`));
|
|
252
275
|
return nodes;
|
|
253
276
|
};
|
|
254
277
|
return /* @__PURE__ */ import_react2.default.createElement(import_material2.Box, { component: "pre", sx: { m: 0, fontFamily: "monospace", fontSize: "0.75rem", whiteSpace: "pre-wrap", wordBreak: "break-all", overflowX: "auto" } }, render());
|
|
@@ -263,14 +286,56 @@
|
|
|
263
286
|
const [boxTop, setBoxTop] = (0, import_react3.useState)(0);
|
|
264
287
|
const [, forceRender] = (0, import_react3.useState)(0);
|
|
265
288
|
const [copied, setCopied] = (0, import_react3.useState)(null);
|
|
289
|
+
const [search, setSearch] = (0, import_react3.useState)("");
|
|
290
|
+
const [matchPos, setMatchPos] = (0, import_react3.useState)(-1);
|
|
291
|
+
const [expanded, setExpanded] = (0, import_react3.useState)(/* @__PURE__ */ new Set());
|
|
292
|
+
const cardRefs = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
|
|
293
|
+
const searchText = (0, import_react3.useRef)(/* @__PURE__ */ new WeakMap());
|
|
266
294
|
(0, import_react3.useEffect)(() => {
|
|
267
295
|
if (boxRef.current) setBoxTop(boxRef.current.getBoundingClientRect().top);
|
|
268
296
|
});
|
|
269
297
|
const clear = () => {
|
|
270
298
|
data.events = [];
|
|
271
299
|
data.signals = [];
|
|
300
|
+
setExpanded(/* @__PURE__ */ new Set());
|
|
301
|
+
cardRefs.current.clear();
|
|
302
|
+
setMatchPos(-1);
|
|
272
303
|
forceRender((n) => n + 1);
|
|
273
304
|
};
|
|
305
|
+
const textOf = (event) => {
|
|
306
|
+
const cached = searchText.current.get(event);
|
|
307
|
+
if (cached !== void 0) return cached;
|
|
308
|
+
let text;
|
|
309
|
+
try {
|
|
310
|
+
text = (event.providerId + " " + JSON.stringify(event.event)).toLowerCase();
|
|
311
|
+
} catch {
|
|
312
|
+
text = event.providerId.toLowerCase();
|
|
313
|
+
}
|
|
314
|
+
searchText.current.set(event, text);
|
|
315
|
+
return text;
|
|
316
|
+
};
|
|
317
|
+
const matches = () => {
|
|
318
|
+
const needle = search.trim().toLowerCase();
|
|
319
|
+
if (needle === "") return [];
|
|
320
|
+
return data.events.filter((event) => textOf(event).includes(needle));
|
|
321
|
+
};
|
|
322
|
+
const isMatch = (event) => {
|
|
323
|
+
const needle = search.trim().toLowerCase();
|
|
324
|
+
return needle !== "" && textOf(event).includes(needle);
|
|
325
|
+
};
|
|
326
|
+
const goToMatch = (pos) => {
|
|
327
|
+
const found2 = matches();
|
|
328
|
+
if (found2.length === 0) return;
|
|
329
|
+
const next = (pos % found2.length + found2.length) % found2.length;
|
|
330
|
+
const target = found2[next];
|
|
331
|
+
setMatchPos(next);
|
|
332
|
+
setExpanded((prev) => new Set(prev).add(target));
|
|
333
|
+
setTimeout(() => {
|
|
334
|
+
const card = cardRefs.current.get(target);
|
|
335
|
+
const hit = card?.querySelector("[data-pd-hit]");
|
|
336
|
+
(hit ?? card)?.scrollIntoView({ block: "center" });
|
|
337
|
+
}, 0);
|
|
338
|
+
};
|
|
274
339
|
const writeClipboard = async (text) => {
|
|
275
340
|
try {
|
|
276
341
|
await navigator.clipboard.writeText(text);
|
|
@@ -295,7 +360,7 @@
|
|
|
295
360
|
writeClipboard(JSON.stringify(event.event, null, 2)).then((ok) => {
|
|
296
361
|
if (!ok) return;
|
|
297
362
|
setCopied(event);
|
|
298
|
-
setTimeout(() => setCopied((
|
|
363
|
+
setTimeout(() => setCopied((current2) => current2 === event ? null : current2), 1500);
|
|
299
364
|
});
|
|
300
365
|
};
|
|
301
366
|
const summaryOf = (event) => {
|
|
@@ -311,14 +376,86 @@
|
|
|
311
376
|
if (data.providers.length === 0) return /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "body2", color: "text.secondary" }, "No providers running.");
|
|
312
377
|
return /* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "row", spacing: 1, flexWrap: "wrap", useFlexGap: true }, data.providers.map((p) => /* @__PURE__ */ import_react3.default.createElement(import_material3.Chip, { key: p.id, label: p.id, size: "small", variant: p.id === instanceConfig.providerId ? "filled" : "outlined" })));
|
|
313
378
|
};
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
379
|
+
const toggle = (event) => setExpanded((prev) => {
|
|
380
|
+
const next = new Set(prev);
|
|
381
|
+
if (next.has(event)) next.delete(event);
|
|
382
|
+
else next.add(event);
|
|
383
|
+
return next;
|
|
384
|
+
});
|
|
385
|
+
const formatEvent = (event, index, current2) => {
|
|
386
|
+
const open = expanded.has(event);
|
|
387
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
388
|
+
import_material3.Paper,
|
|
389
|
+
{
|
|
390
|
+
key: index,
|
|
391
|
+
variant: "outlined",
|
|
392
|
+
ref: (el) => {
|
|
393
|
+
if (el) cardRefs.current.set(event, el);
|
|
394
|
+
else cardRefs.current.delete(event);
|
|
395
|
+
},
|
|
396
|
+
sx: { mb: 0.5, ...current2 === event ? { outline: 2, outlineColor: "warning.main", outlineOffset: -2 } : {} }
|
|
397
|
+
},
|
|
398
|
+
/* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "row", spacing: 1.5, alignItems: "center", sx: { minWidth: 0, px: 1, py: 0.5, cursor: "pointer" }, onClick: () => toggle(event) }, /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { size: "small", "aria-label": open ? "Collapse event" : "Expand event", onClick: (e) => {
|
|
399
|
+
e.stopPropagation();
|
|
400
|
+
toggle(event);
|
|
401
|
+
} }, open ? /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.ExpandLess, { fontSize: "small" }) : /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.ExpandMore, { fontSize: "small" })), /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "caption", color: isMatch(event) ? "warning.main" : "text.secondary", sx: { fontFamily: "monospace" } }, new Date(event.ts).toISOString()), /* @__PURE__ */ import_react3.default.createElement(import_material3.Chip, { label: event.providerId, size: "small", variant: "outlined", sx: { fontSize: "0.65rem", height: 18 } }), /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary", noWrap: true }, summaryOf(event.event)), /* @__PURE__ */ import_react3.default.createElement(import_material3.Tooltip, { title: copied === event ? "Copied" : "Copy event JSON" }, /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { size: "small", "aria-label": "Copy event JSON", sx: { ml: "auto" }, onClick: (e) => {
|
|
402
|
+
e.stopPropagation();
|
|
403
|
+
copy(event);
|
|
404
|
+
} }, copied === event ? /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Check, { fontSize: "small", color: "success" }) : /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.ContentCopy, { fontSize: "small" })))),
|
|
405
|
+
open && /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { sx: { px: 2, pb: 1 } }, /* @__PURE__ */ import_react3.default.createElement(JsonBlock, { value: event.event, highlight: search }))
|
|
406
|
+
);
|
|
407
|
+
};
|
|
318
408
|
if (!data.started) {
|
|
319
409
|
return /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { sx: { p: 2 } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { color: "text.secondary" }, "Provider Debug not started. Start the channel (tab settings \u2699 \u2192 Start) to subscribe to a provider and watch its raw events."));
|
|
320
410
|
}
|
|
321
|
-
|
|
411
|
+
const found = matches();
|
|
412
|
+
const current = matchPos >= 0 && matchPos < found.length ? found[matchPos] : void 0;
|
|
413
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_material3.Card, { sx: { flex: 1, width: "98%", alignSelf: "center", m: 1 } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.CardHeader, { title: /* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "row", alignItems: "center", sx: { width: "100%" } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { mr: 4 }, /* @__PURE__ */ import_react3.default.createElement("b", null, "Provider:"), " ", instanceConfig.providerId || "(none)"), /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { mr: 4 }, /* @__PURE__ */ import_react3.default.createElement("b", null, "Events:"), " ", data.events.length, " / ", config.maxEvents), /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { mr: 4 }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Info, { fontSize: "small", sx: { mb: 0.25 } }), /* @__PURE__ */ import_react3.default.createElement("b", null, "\xA0Status:"), " ", data.paused ? "paused" : data.started ? "started" : "stopped"), /* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "row", alignItems: "center", spacing: 0.5, sx: { ml: "auto" } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "caption", color: found.length === 0 && search.trim() !== "" ? "warning.main" : "text.secondary", sx: { minWidth: 44, textAlign: "right" } }, `${matchPos + 1}/${found.length}`), /* @__PURE__ */ import_react3.default.createElement(
|
|
414
|
+
import_material3.TextField,
|
|
415
|
+
{
|
|
416
|
+
value: search,
|
|
417
|
+
onChange: (e) => {
|
|
418
|
+
setSearch(e.target.value);
|
|
419
|
+
setMatchPos(-1);
|
|
420
|
+
},
|
|
421
|
+
onKeyDown: (e) => {
|
|
422
|
+
if (e.key !== "Enter") return;
|
|
423
|
+
e.preventDefault();
|
|
424
|
+
goToMatch(e.shiftKey ? matchPos - 1 : matchPos + 1);
|
|
425
|
+
},
|
|
426
|
+
placeholder: "Search events",
|
|
427
|
+
variant: "standard",
|
|
428
|
+
sx: { width: 200 },
|
|
429
|
+
slotProps: {
|
|
430
|
+
htmlInput: { "aria-label": "Search events" },
|
|
431
|
+
input: {
|
|
432
|
+
startAdornment: /* @__PURE__ */ import_react3.default.createElement(import_material3.InputAdornment, { position: "start" }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Search, { fontSize: "small" })),
|
|
433
|
+
// siempre presente y deshabilitado: si se renderiza en
|
|
434
|
+
// condicional desaparece bajo el propio click que lo pulsa
|
|
435
|
+
endAdornment: /* @__PURE__ */ import_react3.default.createElement(import_material3.InputAdornment, { position: "end" }, /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { size: "small", "aria-label": "Clear search", disabled: search === "", onClick: () => {
|
|
436
|
+
setSearch("");
|
|
437
|
+
setMatchPos(-1);
|
|
438
|
+
} }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.Clear, { fontSize: "small" })))
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
), /* @__PURE__ */ import_react3.default.createElement(import_material3.Tooltip, { title: "Previous match (Shift+Enter)" }, /* @__PURE__ */ import_react3.default.createElement("span", null, /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { size: "small", "aria-label": "Previous match", disabled: found.length === 0, onClick: () => goToMatch(matchPos - 1) }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.ArrowUpward, { fontSize: "small" })))), /* @__PURE__ */ import_react3.default.createElement(import_material3.Tooltip, { title: "Next match (Enter)" }, /* @__PURE__ */ import_react3.default.createElement("span", null, /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { size: "small", "aria-label": "Next match", disabled: found.length === 0, onClick: () => goToMatch(matchPos + 1) }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.ArrowDownward, { fontSize: "small" })))), /* @__PURE__ */ import_react3.default.createElement(import_material3.Tooltip, { title: "Clear captured events" }, /* @__PURE__ */ import_react3.default.createElement("span", null, /* @__PURE__ */ import_react3.default.createElement(import_material3.IconButton, { size: "small", "aria-label": "Clear captured events", onClick: clear, disabled: data.events.length === 0 && data.signals.length === 0 }, /* @__PURE__ */ import_react3.default.createElement(import_icons_material2.DeleteSweep, { fontSize: "small" })))))) }), /* @__PURE__ */ import_react3.default.createElement(import_material3.CardContent, null, /* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "column", spacing: 1, sx: { mb: 1 } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "row", spacing: 1, alignItems: "center", flexWrap: "wrap", useFlexGap: true }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary" }, "Running providers"), formatProviders(), /* @__PURE__ */ import_react3.default.createElement(import_material3.Stack, { direction: "row", spacing: 1, sx: { ml: "auto" } }, /* @__PURE__ */ import_react3.default.createElement(
|
|
443
|
+
import_material3.Chip,
|
|
444
|
+
{
|
|
445
|
+
label: "config",
|
|
446
|
+
size: "small",
|
|
447
|
+
color: data.configAccepted ? "success" : "default",
|
|
448
|
+
variant: data.configAccepted ? "filled" : "outlined"
|
|
449
|
+
}
|
|
450
|
+
), /* @__PURE__ */ import_react3.default.createElement(
|
|
451
|
+
import_material3.Chip,
|
|
452
|
+
{
|
|
453
|
+
label: "subscribed",
|
|
454
|
+
size: "small",
|
|
455
|
+
color: data.subscribed ? "success" : "default",
|
|
456
|
+
variant: data.subscribed ? "filled" : "outlined"
|
|
457
|
+
}
|
|
458
|
+
))), data.signals.map((s, index) => /* @__PURE__ */ import_react3.default.createElement(import_material3.Typography, { key: index, variant: "caption", color: "text.secondary" }, "*** ", s, " ***"))), /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { ref: boxRef, sx: { display: "flex", flexDirection: "column", overflowY: "auto", overflowX: "hidden", width: "100%", flexGrow: 1, height: `calc(100vh - ${boxTop}px - 35px)` } }, /* @__PURE__ */ import_react3.default.createElement(import_material3.Box, { sx: { flex: 1, overflowY: "auto", ml: 1, mr: 1 } }, data.events.map((e, index) => formatEvent(e, index, current))))));
|
|
322
459
|
};
|
|
323
460
|
|
|
324
461
|
// src/front/ProviderDebugChannel.ts
|
|
@@ -374,8 +511,15 @@
|
|
|
374
511
|
return { action: import_kwirth_common_front.EChannelRefreshAction.REFRESH };
|
|
375
512
|
case import_kwirth_common.EInstanceMessageType.SIGNAL: {
|
|
376
513
|
const signalMessage = JSON.parse(wsEvent.data);
|
|
377
|
-
|
|
378
|
-
|
|
514
|
+
const startResponse = signalMessage.flow === import_kwirth_common.EInstanceMessageFlow.RESPONSE && signalMessage.action === import_kwirth_common.EInstanceMessageAction.START;
|
|
515
|
+
if (startResponse) channelObject.instanceId = signalMessage.instance;
|
|
516
|
+
if (startResponse && signalMessage.level === void 0) {
|
|
517
|
+
data.configAccepted = true;
|
|
518
|
+
return { action: import_kwirth_common_front.EChannelRefreshAction.REFRESH };
|
|
519
|
+
}
|
|
520
|
+
if (startResponse && signalMessage.level === import_kwirth_common.ESignalMessageLevel.INFO) {
|
|
521
|
+
data.subscribed = true;
|
|
522
|
+
return { action: import_kwirth_common_front.EChannelRefreshAction.REFRESH };
|
|
379
523
|
}
|
|
380
524
|
if (signalMessage.text) data.signals.push(signalMessage.text);
|
|
381
525
|
return { action: import_kwirth_common_front.EChannelRefreshAction.REFRESH };
|
|
@@ -394,6 +538,8 @@
|
|
|
394
538
|
const data = channelObject.data;
|
|
395
539
|
data.events = [];
|
|
396
540
|
data.signals = [];
|
|
541
|
+
data.configAccepted = false;
|
|
542
|
+
data.subscribed = false;
|
|
397
543
|
data.paused = false;
|
|
398
544
|
data.started = true;
|
|
399
545
|
return true;
|
|
@@ -410,6 +556,8 @@
|
|
|
410
556
|
}
|
|
411
557
|
stopChannel(channelObject) {
|
|
412
558
|
const data = channelObject.data;
|
|
559
|
+
data.configAccepted = false;
|
|
560
|
+
data.subscribed = false;
|
|
413
561
|
data.paused = false;
|
|
414
562
|
data.started = false;
|
|
415
563
|
return true;
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"id": "provider-debug",
|
|
5
5
|
"name": "@kwirthmagnify/kwirth-plugin-provider-debug",
|
|
6
6
|
"displayName": "Provider Debug",
|
|
7
|
-
"version": "0.1.
|
|
7
|
+
"version": "0.1.2",
|
|
8
8
|
"description": "Provider debugging channel for Kwirth - subscribes to a running provider and streams its raw events",
|
|
9
9
|
"icon": "DataObjectOutlined",
|
|
10
10
|
"requiresRestart": false,
|