poe-svelte-ui-lib 1.73.0 → 1.74.0
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.
|
@@ -8,5 +8,6 @@ export { crc16Kermit, crc8Rohc } from "./crc.js";
|
|
|
8
8
|
export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, type PacketObject, type DecodedPacketObject, type PacketParse, } from "./packet.js";
|
|
9
9
|
export { createPlotGroups, debPlotValues, DEB_PLOT_ARGUMENT, type PlotPoint } from "./plotGroups.js";
|
|
10
10
|
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugCmd, xPoeDebugValueFromText, type XPoeDebugItem } from "./xPoeDebug.js";
|
|
11
|
+
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
|
11
12
|
export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, type CanFrame, type CanMessage, } from "./can.js";
|
|
12
13
|
export { HEADER_CODES, DEFAULT_ARGUMENT_CODES, parseArgumentDictionary, nameByCode } from "./dictionary.js";
|
package/dist/protocols/index.js
CHANGED
|
@@ -16,6 +16,8 @@ export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc,
|
|
|
16
16
|
export { createPlotGroups, debPlotValues, DEB_PLOT_ARGUMENT } from "./plotGroups.js";
|
|
17
17
|
/* System Console over UART: CBOR [SrcID, HdrArg, Value, FHS, CRC16], plus stream parsing */
|
|
18
18
|
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugCmd, xPoeDebugValueFromText } from "./xPoeDebug.js";
|
|
19
|
+
/* Отображение Value xPOE-Debug в таблице/модалке и копирование Value/HEX */
|
|
20
|
+
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
|
19
21
|
/* CAN/CAN FD через slcan-адаптер: нарезка пакета на кадры как в can_service прошивки, кадры slcan, потоковый разбор со сборкой фрагментов */
|
|
20
22
|
export { CAN_FINAL_BIT, CAN_PACKET_ID_MASK, canFragments, encodeSlcanFrame, encodeCanPacket, createCanParser, slcanOpenCommands, } from "./can.js";
|
|
21
23
|
/* Словарь заголовков и аргументов Cmd — по умолчанию из мастер-словаря dictionary.yaml библиотеки, разбор файла словаря */
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function formatXPoeDebugValue(rawValue: string): string;
|
|
2
|
+
export declare function formatXPoeDebugValueForTable(rawValue: string, isCbor?: boolean): string;
|
|
3
|
+
export declare function bindXPoeDebugCopyButtons(): () => void;
|
|
4
|
+
export declare function formatXPoeDebugModal(text: string): string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { cborToValue } from "./cbor/value.js";
|
|
2
|
+
const LOG_LEVELS = { 1: "🔴", 2: "🟡", 3: "🟢", 4: "🔵" };
|
|
3
|
+
const HTML_ESCAPES = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
4
|
+
const escapeHtml = (value) => value.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char]);
|
|
5
|
+
const bytesToHex = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0").toUpperCase()).join(" ");
|
|
6
|
+
const normalizeCborValue = (value) => {
|
|
7
|
+
if (value instanceof Uint8Array)
|
|
8
|
+
return `h'${bytesToHex(value)}'`;
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return value.map(normalizeCborValue);
|
|
11
|
+
if (value && typeof value === "object")
|
|
12
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalizeCborValue(item)]));
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
15
|
+
const compactCborValue = (value) => {
|
|
16
|
+
if (typeof value === "string")
|
|
17
|
+
return escapeHtml(value);
|
|
18
|
+
if (value instanceof Uint8Array)
|
|
19
|
+
return `bytes(${value.length}): ${escapeHtml(bytesToHex(value))}`;
|
|
20
|
+
return escapeHtml(JSON.stringify(normalizeCborValue(value)) ?? String(value));
|
|
21
|
+
};
|
|
22
|
+
/* Декодирует CBOR Value для таблицы; системный лог имеет вид [уровень, тег, текст]. */
|
|
23
|
+
export function formatXPoeDebugValue(rawValue) {
|
|
24
|
+
const hex = rawValue.trim().replace(/\s/g, "");
|
|
25
|
+
if (!hex || hex.length % 2 !== 0 || !/^[0-9A-Fa-f]+$/.test(hex))
|
|
26
|
+
return escapeHtml(rawValue);
|
|
27
|
+
try {
|
|
28
|
+
const value = cborToValue(Uint8Array.from(hex.match(/.{2}/g), (byte) => parseInt(byte, 16)));
|
|
29
|
+
if (Array.isArray(value) &&
|
|
30
|
+
value.length === 3 &&
|
|
31
|
+
typeof value[0] === "number" &&
|
|
32
|
+
LOG_LEVELS[value[0]] &&
|
|
33
|
+
typeof value[1] === "string" &&
|
|
34
|
+
(typeof value[2] === "string" || value[2] instanceof Uint8Array)) {
|
|
35
|
+
const level = LOG_LEVELS[value[0]];
|
|
36
|
+
const detail = compactCborValue(value[2]);
|
|
37
|
+
return `<span class="font-semibold">${level}</span> · ${escapeHtml(value[1])}${detail ? ` · ${detail}` : ""}`;
|
|
38
|
+
}
|
|
39
|
+
return compactCborValue(value);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return escapeHtml(rawValue);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/* Сохраняет исходный HEX скрытым до раскрытия строки в общей таблице. */
|
|
46
|
+
export function formatXPoeDebugValueForTable(rawValue, isCbor = true) {
|
|
47
|
+
if (!isCbor)
|
|
48
|
+
return escapeHtml(rawValue);
|
|
49
|
+
const formatted = formatXPoeDebugValue(rawValue);
|
|
50
|
+
return formatted === escapeHtml(rawValue) ? formatted : `${formatted}<span class="hidden">\nHEX\n${escapeHtml(rawValue)}</span>`;
|
|
51
|
+
}
|
|
52
|
+
const COPY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-width="1.5"><path d="M6 11c0-2.828 0-4.243.879-5.121C7.757 5 9.172 5 12 5h3c2.828 0 4.243 0 5.121.879C21 6.757 21 8.172 21 11v5c0 2.828 0 4.243-.879 5.121C19.243 22 17.828 22 15 22h-3c-2.828 0-4.243 0-5.121-.879C6 20.243 6 18.828 6 16z"/><path d="M6 19a3 3 0 0 1-3-3v-6c0-3.771 0-5.657 1.172-6.828S7.229 2 11 2h4a3 3 0 0 1 3 3"/></g></svg>`;
|
|
53
|
+
const COPIED_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4L19 6" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
54
|
+
let copyBindings = 0;
|
|
55
|
+
const handleCopyClick = (event) => {
|
|
56
|
+
if (!(event.target instanceof Element))
|
|
57
|
+
return;
|
|
58
|
+
const button = event.target.closest("button[data-xpoe-copy]");
|
|
59
|
+
if (!button)
|
|
60
|
+
return;
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
event.stopPropagation();
|
|
63
|
+
const value = button.closest("section")?.querySelector("pre")?.textContent ?? "";
|
|
64
|
+
const label = button.getAttribute("aria-label") ?? "Копировать";
|
|
65
|
+
navigator.clipboard
|
|
66
|
+
.writeText(value)
|
|
67
|
+
.then(() => {
|
|
68
|
+
button.innerHTML = COPIED_ICON;
|
|
69
|
+
button.setAttribute("aria-label", "Скопировано");
|
|
70
|
+
window.setTimeout(() => {
|
|
71
|
+
button.innerHTML = COPY_ICON;
|
|
72
|
+
button.setAttribute("aria-label", label);
|
|
73
|
+
}, 1300);
|
|
74
|
+
})
|
|
75
|
+
.catch(() => {
|
|
76
|
+
button.setAttribute("aria-label", "Не удалось скопировать");
|
|
77
|
+
window.setTimeout(() => button.setAttribute("aria-label", label), 1300);
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
/* Делегированный обработчик кнопок модального окна; возвращённую функцию вызывают при уничтожении страницы. */
|
|
81
|
+
export function bindXPoeDebugCopyButtons() {
|
|
82
|
+
if (copyBindings++ === 0)
|
|
83
|
+
document.addEventListener("click", handleCopyClick);
|
|
84
|
+
let isBound = true;
|
|
85
|
+
return () => {
|
|
86
|
+
if (!isBound)
|
|
87
|
+
return;
|
|
88
|
+
isBound = false;
|
|
89
|
+
if (--copyBindings === 0)
|
|
90
|
+
document.removeEventListener("click", handleCopyClick);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/* Две прокручиваемые секции модалки; HEX-раздел появляется, если форматтер таблицы добавил исходные байты. */
|
|
94
|
+
export function formatXPoeDebugModal(text) {
|
|
95
|
+
const separator = "\nHEX\n";
|
|
96
|
+
const hexIndex = text.lastIndexOf(separator);
|
|
97
|
+
const decoded = hexIndex < 0 ? text : text.slice(0, hexIndex);
|
|
98
|
+
const hex = hexIndex < 0 ? "" : text.slice(hexIndex + separator.length);
|
|
99
|
+
const modalStyle = "display:flex;flex-direction:column;gap:.75rem;max-height:80vh;min-width:0;overflow-y:auto;overflow-x:hidden";
|
|
100
|
+
const sectionStyle = "min-width:0;padding:.25rem 0 .75rem";
|
|
101
|
+
const headingStyle = "display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin:0 0 .5rem;font-weight:600";
|
|
102
|
+
const buttonStyle = "display:flex;flex:0 0 28px;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:0;border-radius:.5rem;background:transparent;color:var(--faint-color);cursor:pointer";
|
|
103
|
+
const buttonClass = "flex size-7 cursor-pointer items-center justify-center rounded-lg border-none bg-transparent text-(--faint-color) transition-colors duration-150 hover:bg-(--container-color) hover:text-(--font-color)";
|
|
104
|
+
const textStyle = "display:block;min-width:0;max-width:100%;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;font:inherit";
|
|
105
|
+
const pane = (title, target, value) => `<section style="${sectionStyle}"><div style="${headingStyle}"><span>${title}</span><button type="button" class="${buttonClass}" data-xpoe-copy="${target}" aria-label="Копировать ${title}" style="${buttonStyle}">${COPY_ICON}</button></div><pre style="${textStyle}">${value}</pre></section>`;
|
|
106
|
+
return `<style>[data-modal]:has([data-xpoe-debug-modal])>div:last-child{display:none!important}</style><div data-xpoe-debug-modal style="${modalStyle}">${pane("Value", "value", decoded)}${hexIndex < 0 ? "" : pane("HEX", "hex", hex)}</div>`;
|
|
107
|
+
}
|