poe-svelte-ui-lib 1.74.0 → 1.74.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/dist/protocols/cbor/encoder.d.ts +1 -0
- package/dist/protocols/cbor/encoder.js +5 -0
- package/dist/protocols/index.js +1 -1
- package/dist/protocols/packet.d.ts +2 -1
- package/dist/protocols/packet.js +36 -14
- package/dist/protocols/xPoeDebug.d.ts +2 -1
- package/dist/protocols/xPoeDebug.js +46 -24
- package/dist/protocols/xPoeDebugView.js +21 -3
- package/package.json +1 -1
|
@@ -16,6 +16,7 @@ export declare class CborEncoder {
|
|
|
16
16
|
encodeFloat16(value: number): this;
|
|
17
17
|
encodeString(value: string): this;
|
|
18
18
|
encodeBytes(value: Uint8Array | number[]): this;
|
|
19
|
+
encodeRaw(value: Uint8Array): this;
|
|
19
20
|
encodeArrayStart(count: number | null): this;
|
|
20
21
|
encodeMapStart(count: number | null): this;
|
|
21
22
|
encodeBreak(): this;
|
|
@@ -211,6 +211,11 @@ export class CborEncoder {
|
|
|
211
211
|
this.writeBytes(bytes);
|
|
212
212
|
return this;
|
|
213
213
|
}
|
|
214
|
+
/* Добавить заранее проверенный CBOR item без повторного кодирования. */
|
|
215
|
+
encodeRaw(value) {
|
|
216
|
+
this.writeBytes(Array.from(value));
|
|
217
|
+
return this;
|
|
218
|
+
}
|
|
214
219
|
/* Начать кодирование массива - count элементов или null для неопределённой длины */
|
|
215
220
|
encodeArrayStart(count) {
|
|
216
221
|
if (count === null)
|
package/dist/protocols/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export { crc16Kermit, crc8Rohc } from "./crc.js";
|
|
|
14
14
|
export { BROADCAST_BUS_ID, makeCmd, splitCmd, EXTERNAL_BUS_ID, packetObjectCrc, encodePacket, decodePacket, objectsFor, createPacketStreamParser, } from "./packet.js";
|
|
15
15
|
/* Графики отладки: кадр OK! DebPlot (SC_PLOT прошивки) — значения из CBOR-карты, график на группу, серии в порядке устройства */
|
|
16
16
|
export { createPlotGroups, debPlotValues, DEB_PLOT_ARGUMENT } from "./plotGroups.js";
|
|
17
|
-
/* System Console over UART:
|
|
17
|
+
/* System Console over UART: single-record poe_cbor_pkt with debug-only FHS */
|
|
18
18
|
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugCmd, xPoeDebugValueFromText } from "./xPoeDebug.js";
|
|
19
19
|
/* Отображение Value xPOE-Debug в таблице/модалке и копирование Value/HEX */
|
|
20
20
|
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
|
@@ -19,7 +19,8 @@ export declare const splitCmd: (cmd: number) => {
|
|
|
19
19
|
argument: number;
|
|
20
20
|
reply: boolean;
|
|
21
21
|
};
|
|
22
|
-
export declare const packetObjectCrc: (
|
|
22
|
+
export declare const packetObjectCrc: (encodedRecordPrefix: Uint8Array) => number;
|
|
23
|
+
export declare const encodePacketBytesPayload: (bytes: Uint8Array) => Uint8Array;
|
|
23
24
|
export declare const encodePacket: (objects: PacketObject[]) => Uint8Array;
|
|
24
25
|
export type PacketParse = {
|
|
25
26
|
status: "complete";
|
package/dist/protocols/packet.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* $lib/protocols/packet.ts */
|
|
2
|
-
/*
|
|
3
|
-
|
|
4
|
-
(по 2 байта big-endian) и Payload — не зависит от того, как числа записаны в CBOR. Общая точка сборки и разбора для терминала (CAN, RS485) и облака */
|
|
2
|
+
/* Общий пакет poe_cbor_pkt: indefinite ARRAY записей [TrgBusID, SrcBusID, Cmd, Payload, CRC16].
|
|
3
|
+
Payload — ровно один CBOR item. CRC-16/KERMIT считается по сырым CBOR-байтам записи от её заголовка до конца Payload. */
|
|
5
4
|
import { CborDecoder } from "./cbor/decoder.js";
|
|
6
5
|
import { CborEncoder } from "./cbor/encoder.js";
|
|
7
6
|
import { CborEndOfDataError } from "./cbor/types.js";
|
|
@@ -17,22 +16,31 @@ export const splitCmd = (cmd) => ({
|
|
|
17
16
|
argument: (cmd >> 1) & 0x1fff,
|
|
18
17
|
reply: (cmd & 1) === 1,
|
|
19
18
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export const packetObjectCrc = (encodedRecordPrefix) => crc16Kermit(0, encodedRecordPrefix);
|
|
20
|
+
/* Кодирует сырые байты как один CBOR byte string для Payload. */
|
|
21
|
+
export const encodePacketBytesPayload = (bytes) => {
|
|
22
|
+
const encoder = new CborEncoder();
|
|
23
|
+
encoder.encodeBytes(bytes);
|
|
24
|
+
return encoder.getResult();
|
|
24
25
|
};
|
|
25
26
|
/* Пакет из одного или нескольких объектов — те же байты, что CBOR_PACK/CBOR_PktEncFinish прошивки */
|
|
26
27
|
export const encodePacket = (objects) => {
|
|
27
28
|
const encoder = new CborEncoder();
|
|
28
29
|
encoder.encodeArrayStart(null);
|
|
29
30
|
for (const { trgBusID, srcBusID, cmd, payload } of objects) {
|
|
31
|
+
const recordStart = encoder.getResult().length;
|
|
30
32
|
encoder.encodeArrayStart(5);
|
|
31
33
|
encoder.encodeUnsigned(trgBusID);
|
|
32
34
|
encoder.encodeUnsigned(srcBusID);
|
|
33
35
|
encoder.encodeUnsigned(cmd);
|
|
34
|
-
encoder.
|
|
35
|
-
|
|
36
|
+
const prefixStart = encoder.getResult().length;
|
|
37
|
+
/* Payload is already CBOR-encoded; reject malformed or concatenated values. */
|
|
38
|
+
const payloadDecoder = new CborDecoder(payload);
|
|
39
|
+
if (!payloadDecoder.skipValue() || payloadDecoder.offset !== payload.length)
|
|
40
|
+
throw new Error("Payload должен содержать ровно один CBOR item");
|
|
41
|
+
encoder.encodeRaw(payload);
|
|
42
|
+
const prefix = encoder.getResult().slice(recordStart, prefixStart + payload.length);
|
|
43
|
+
encoder.encodeUnsigned(packetObjectCrc(prefix));
|
|
36
44
|
}
|
|
37
45
|
encoder.encodeBreak();
|
|
38
46
|
return encoder.getResult();
|
|
@@ -52,13 +60,27 @@ const readValue = (bytes, offset) => {
|
|
|
52
60
|
};
|
|
53
61
|
const toNumber = (value) => (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 0xffff ? value : null);
|
|
54
62
|
/* Объект пакета; не по формату (не 5 элементов, не те типы) — null, как CBOR_PktDecNext прошивки его пропускает */
|
|
55
|
-
const toObject = (value) => {
|
|
63
|
+
const toObject = (value, rawRecord) => {
|
|
56
64
|
if (!Array.isArray(value) || value.length !== 5)
|
|
57
65
|
return null;
|
|
58
|
-
const [trgBusID, srcBusID, cmd,
|
|
59
|
-
if (trgBusID === null || srcBusID === null || cmd === null ||
|
|
66
|
+
const [trgBusID, srcBusID, cmd, crc] = [toNumber(value[0]), toNumber(value[1]), toNumber(value[2]), toNumber(value[4])];
|
|
67
|
+
if (trgBusID === null || srcBusID === null || cmd === null || crc === null)
|
|
68
|
+
return null;
|
|
69
|
+
const info = rawRecord[0] & 0x1f;
|
|
70
|
+
const headerLength = info < 24 ? 1 : info === 24 ? 2 : info === 25 ? 3 : info === 26 ? 5 : info === 27 ? 9 : 0;
|
|
71
|
+
if ((rawRecord[0] >> 5) !== 4 || headerLength === 0)
|
|
72
|
+
return null;
|
|
73
|
+
const fields = new CborDecoder(rawRecord.subarray(headerLength));
|
|
74
|
+
for (let i = 0; i < 3; i++)
|
|
75
|
+
if (!fields.skipValue())
|
|
76
|
+
return null;
|
|
77
|
+
const payloadStart = headerLength + fields.offset;
|
|
78
|
+
if (!fields.skipValue())
|
|
60
79
|
return null;
|
|
61
|
-
|
|
80
|
+
const payloadEnd = headerLength + fields.offset;
|
|
81
|
+
const prefix = rawRecord.subarray(0, payloadEnd);
|
|
82
|
+
const payloadBytes = rawRecord.subarray(payloadStart, payloadEnd);
|
|
83
|
+
return { trgBusID, srcBusID, cmd, payload: payloadBytes, crc, crcOk: packetObjectCrc(prefix) === crc, ...splitCmd(cmd) };
|
|
62
84
|
};
|
|
63
85
|
/* Разбор пакета с начала буфера — и для целого кадра (CAN), и для потока (RS485: «incomplete» — ждать ещё байт, «garbage» — сдвинуться на байт) */
|
|
64
86
|
export const decodePacket = (bytes) => {
|
|
@@ -93,7 +115,7 @@ export const decodePacket = (bytes) => {
|
|
|
93
115
|
if (!value)
|
|
94
116
|
return { status: "incomplete" };
|
|
95
117
|
offset += value.length;
|
|
96
|
-
const object = toObject(value.value);
|
|
118
|
+
const object = toObject(value.value, bytes.subarray(offset - value.length, offset));
|
|
97
119
|
if (object)
|
|
98
120
|
objects.push(object);
|
|
99
121
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type XPoeDebugItem = {
|
|
2
2
|
type: "frame";
|
|
3
|
+
trgId: number;
|
|
3
4
|
srcId: number;
|
|
4
5
|
cmd: number;
|
|
5
6
|
header: string;
|
|
@@ -14,7 +15,7 @@ export type XPoeDebugItem = {
|
|
|
14
15
|
text: string;
|
|
15
16
|
};
|
|
16
17
|
export declare const xPoeDebugCmd: (header: string, argument: string) => number;
|
|
17
|
-
export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number, freeHeap?: number | null) => Uint8Array;
|
|
18
|
+
export declare const encodeXPoeDebugFrame: (header: string, argument: string, value: Uint8Array, srcId?: number, freeHeap?: number | null, trgId?: number) => Uint8Array;
|
|
18
19
|
export declare const createXPoeDebugParser: () => {
|
|
19
20
|
push(chunk: Uint8Array): XPoeDebugItem[];
|
|
20
21
|
reset(): void;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
/*
|
|
2
|
-
first four array items, including the array header. UART is a stream; the definite array is self-delimiting. */
|
|
1
|
+
/* Debug is a one-record poe_cbor_pkt. Its record adds FHS to the common bus fields. */
|
|
3
2
|
import { CborDecoder } from "./cbor/decoder.js";
|
|
4
3
|
import { CborEndOfDataError } from "./cbor/types.js";
|
|
5
4
|
import { crc16Kermit } from "./crc.js";
|
|
@@ -8,7 +7,8 @@ import { DEFAULT_ARGUMENT_CODES, HEADER_CODES, nameByCode } from "./dictionary.j
|
|
|
8
7
|
import { makeCmd, splitCmd } from "./packet.js";
|
|
9
8
|
const MAX_TEXT_TAIL = 4096;
|
|
10
9
|
const MAX_FRAME = 8192;
|
|
11
|
-
const
|
|
10
|
+
const PACKET_START = 0x9f;
|
|
11
|
+
const RECORD6 = 0x86;
|
|
12
12
|
const decoder = new TextDecoder();
|
|
13
13
|
const encodeUInt = (input) => {
|
|
14
14
|
const value = BigInt(input);
|
|
@@ -19,7 +19,7 @@ const encodeUInt = (input) => {
|
|
|
19
19
|
if (value <= 0xffffn)
|
|
20
20
|
return [0x19, Number(value >> 8n), Number(value & 0xffn)];
|
|
21
21
|
if (value <= 0xffffffffn)
|
|
22
|
-
return [0x1a, Number(value >> 24n), Number(value >> 16n & 0xffn), Number(value >> 8n & 0xffn), Number(value & 0xffn)];
|
|
22
|
+
return [0x1a, Number(value >> 24n), Number((value >> 16n) & 0xffn), Number((value >> 8n) & 0xffn), Number(value & 0xffn)];
|
|
23
23
|
throw new Error("xPOE-Debug integer is out of range");
|
|
24
24
|
};
|
|
25
25
|
export const xPoeDebugCmd = (header, argument) => {
|
|
@@ -30,47 +30,67 @@ export const xPoeDebugCmd = (header, argument) => {
|
|
|
30
30
|
return makeCmd(hdr, arg);
|
|
31
31
|
};
|
|
32
32
|
/* Команда терминала: CRC считается по тем CBOR-байтам, которые реально отправляются. */
|
|
33
|
-
export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0, freeHeap = null) => {
|
|
33
|
+
export const encodeXPoeDebugFrame = (header, argument, value, srcId = 0, freeHeap = null, trgId = 0) => {
|
|
34
34
|
const cmd = xPoeDebugCmd(header, argument);
|
|
35
|
-
if (!
|
|
36
|
-
throw new Error("
|
|
35
|
+
if (![srcId, trgId, cmd].every((n) => Number.isInteger(n) && n >= 0 && n <= 0xffff))
|
|
36
|
+
throw new Error("TrgBusID, SrcBusID и Cmd должны быть uint16");
|
|
37
37
|
const valueParser = new CborDecoder(value);
|
|
38
38
|
if (!valueParser.skipValue() || valueParser.offset !== value.length)
|
|
39
39
|
throw new Error("Value должен содержать ровно один CBOR item");
|
|
40
|
-
const
|
|
41
|
-
const crc = crc16Kermit(0,
|
|
42
|
-
return new Uint8Array([...
|
|
40
|
+
const recordPrefix = new Uint8Array([RECORD6, ...encodeUInt(trgId), ...encodeUInt(srcId), ...encodeUInt(cmd), ...value, ...(freeHeap === null ? [0xf6] : encodeUInt(freeHeap))]);
|
|
41
|
+
const crc = crc16Kermit(0, recordPrefix);
|
|
42
|
+
return new Uint8Array([PACKET_START, ...recordPrefix, ...encodeUInt(crc), 0xff]);
|
|
43
43
|
};
|
|
44
|
-
const textItems = (bytes) => decoder
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const textItems = (bytes) => decoder
|
|
45
|
+
.decode(bytes)
|
|
46
|
+
.split("\n")
|
|
47
|
+
.map((line) => line.trim())
|
|
48
|
+
.filter((line) => line && ![...line].some((char) => char.charCodeAt(0) < 0x20 && char !== "\t"))
|
|
49
|
+
.map((text) => ({ type: "raw", text }));
|
|
50
|
+
const readUInt = (value) => (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 0xffff ? value : null);
|
|
51
|
+
/* Декодирует пакет из одной записи с FHS; null означает неполный или невалидный кадр. */
|
|
47
52
|
const frameItem = (buffer) => {
|
|
48
|
-
if (buffer[0] !==
|
|
53
|
+
if (buffer[0] !== PACKET_START || buffer[1] !== RECORD6)
|
|
49
54
|
return { status: "garbage" };
|
|
50
55
|
try {
|
|
51
56
|
const cbor = new CborDecoder(buffer);
|
|
52
|
-
cbor.decode(); /* Проверка структуры и границы всего
|
|
57
|
+
const packet = cbor.decode(); /* Проверка структуры и границы всего пакета */
|
|
58
|
+
if (!Array.isArray(packet) || packet.length !== 1 || !Array.isArray(packet[0]) || packet[0].length !== 6)
|
|
59
|
+
return { status: "garbage" };
|
|
53
60
|
const length = cbor.offset;
|
|
54
|
-
const fields = new CborDecoder(buffer.subarray(
|
|
61
|
+
const fields = new CborDecoder(buffer.subarray(2, length));
|
|
62
|
+
const trgId = readUInt(fields.decode());
|
|
55
63
|
const srcId = readUInt(fields.decode());
|
|
56
64
|
const cmd = readUInt(fields.decode());
|
|
57
|
-
if (srcId === null || cmd === null)
|
|
65
|
+
if (trgId === null || srcId === null || cmd === null)
|
|
58
66
|
return { status: "garbage" };
|
|
59
|
-
const valueStart =
|
|
67
|
+
const valueStart = 2 + fields.offset;
|
|
60
68
|
if (!fields.skipValue())
|
|
61
69
|
return { status: "garbage" };
|
|
62
|
-
const valueEnd =
|
|
70
|
+
const valueEnd = 2 + fields.offset;
|
|
63
71
|
const freeHeapValue = fields.decode();
|
|
64
|
-
const crcStart =
|
|
72
|
+
const crcStart = 2 + fields.offset;
|
|
65
73
|
const crc = readUInt(fields.decode());
|
|
66
|
-
if (fields.offset !== length -
|
|
74
|
+
if (fields.offset !== length - 3 || crc === null || (freeHeapValue !== null && (typeof freeHeapValue !== "number" || !Number.isInteger(freeHeapValue) || freeHeapValue < 0)))
|
|
67
75
|
return { status: "garbage" };
|
|
68
76
|
const valueBytes = buffer.slice(valueStart, valueEnd);
|
|
69
77
|
const { header, argument } = splitCmd(cmd);
|
|
70
78
|
return {
|
|
71
79
|
status: "complete",
|
|
72
80
|
length,
|
|
73
|
-
item: {
|
|
81
|
+
item: {
|
|
82
|
+
type: "frame",
|
|
83
|
+
trgId,
|
|
84
|
+
srcId,
|
|
85
|
+
cmd,
|
|
86
|
+
header: Object.entries(HEADER_CODES).find(([, code]) => code === header)?.[0] ?? String(header),
|
|
87
|
+
argument: nameByCode(DEFAULT_ARGUMENT_CODES, argument),
|
|
88
|
+
value: Array.from(valueBytes, (byte) => byte.toString(16).padStart(2, "0").toUpperCase()).join(""),
|
|
89
|
+
valueBytes,
|
|
90
|
+
crc: crc.toString(16).toUpperCase().padStart(4, "0"),
|
|
91
|
+
crcOk: crc16Kermit(0, buffer.subarray(1, crcStart)) === crc,
|
|
92
|
+
freeHeap: typeof freeHeapValue === "number" ? String(freeHeapValue) : "",
|
|
93
|
+
},
|
|
74
94
|
};
|
|
75
95
|
}
|
|
76
96
|
catch (error) {
|
|
@@ -92,7 +112,7 @@ export const createXPoeDebugParser = () => {
|
|
|
92
112
|
buffer = concat(buffer, chunk);
|
|
93
113
|
const items = [];
|
|
94
114
|
while (buffer.length) {
|
|
95
|
-
const start = buffer.indexOf(
|
|
115
|
+
const start = buffer.indexOf(PACKET_START);
|
|
96
116
|
if (start < 0) {
|
|
97
117
|
const cut = buffer.length > MAX_TEXT_TAIL ? buffer.length : buffer.lastIndexOf(0x0a);
|
|
98
118
|
if (cut >= 0) {
|
|
@@ -124,7 +144,9 @@ export const createXPoeDebugParser = () => {
|
|
|
124
144
|
}
|
|
125
145
|
return items;
|
|
126
146
|
},
|
|
127
|
-
reset() {
|
|
147
|
+
reset() {
|
|
148
|
+
buffer = new Uint8Array();
|
|
149
|
+
},
|
|
128
150
|
};
|
|
129
151
|
};
|
|
130
152
|
/* Value form input: ready CBOR hex passes through; JSON is converted to CBOR. */
|
|
@@ -52,6 +52,17 @@ export function formatXPoeDebugValueForTable(rawValue, isCbor = true) {
|
|
|
52
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
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
54
|
let copyBindings = 0;
|
|
55
|
+
let copyObserver = null;
|
|
56
|
+
const removeXPoeDebugModalFooters = () => {
|
|
57
|
+
document.querySelectorAll("[data-modal]").forEach((modal) => {
|
|
58
|
+
if (!modal.querySelector("[data-xpoe-debug-modal]"))
|
|
59
|
+
return;
|
|
60
|
+
const footer = modal.lastElementChild;
|
|
61
|
+
if (!footer || modal.children.length < 3)
|
|
62
|
+
return;
|
|
63
|
+
footer.remove();
|
|
64
|
+
});
|
|
65
|
+
};
|
|
55
66
|
const handleCopyClick = (event) => {
|
|
56
67
|
if (!(event.target instanceof Element))
|
|
57
68
|
return;
|
|
@@ -79,15 +90,22 @@ const handleCopyClick = (event) => {
|
|
|
79
90
|
};
|
|
80
91
|
/* Делегированный обработчик кнопок модального окна; возвращённую функцию вызывают при уничтожении страницы. */
|
|
81
92
|
export function bindXPoeDebugCopyButtons() {
|
|
82
|
-
if (copyBindings++ === 0)
|
|
93
|
+
if (copyBindings++ === 0) {
|
|
83
94
|
document.addEventListener("click", handleCopyClick);
|
|
95
|
+
removeXPoeDebugModalFooters();
|
|
96
|
+
copyObserver = new MutationObserver(removeXPoeDebugModalFooters);
|
|
97
|
+
copyObserver.observe(document.documentElement, { childList: true, subtree: true });
|
|
98
|
+
}
|
|
84
99
|
let isBound = true;
|
|
85
100
|
return () => {
|
|
86
101
|
if (!isBound)
|
|
87
102
|
return;
|
|
88
103
|
isBound = false;
|
|
89
|
-
if (--copyBindings === 0)
|
|
104
|
+
if (--copyBindings === 0) {
|
|
90
105
|
document.removeEventListener("click", handleCopyClick);
|
|
106
|
+
copyObserver?.disconnect();
|
|
107
|
+
copyObserver = null;
|
|
108
|
+
}
|
|
91
109
|
};
|
|
92
110
|
}
|
|
93
111
|
/* Две прокручиваемые секции модалки; HEX-раздел появляется, если форматтер таблицы добавил исходные байты. */
|
|
@@ -103,5 +121,5 @@ export function formatXPoeDebugModal(text) {
|
|
|
103
121
|
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
122
|
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
123
|
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 `<
|
|
124
|
+
return `<div data-xpoe-debug-modal style="${modalStyle}">${pane("Value", "value", decoded)}${hexIndex < 0 ? "" : pane("HEX", "hex", hex)}</div>`;
|
|
107
125
|
}
|