poe-svelte-ui-lib 1.75.0 → 1.77.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.
- package/README.md +1 -1
- package/dist/FileAttach/FileAttach.svelte +13 -1
- package/dist/FileAttach/README.md +32 -18
- package/dist/WidgetMap/WidgetMap.svelte +12 -2
- package/dist/WidgetMap/mapWrapper/Line.svelte +21 -16
- package/dist/WidgetMap/mapWrapper/MapLibre.svelte +9 -1
- package/dist/WidgetMap/mapWrapper/MapLibre.svelte.d.ts +1 -0
- package/dist/WidgetStackInfo/README.md +1 -1
- package/dist/protocols/PROTOCOL.md +113 -0
- package/dist/protocols/cbor/json.d.ts +0 -11
- package/dist/protocols/cbor/json.js +4 -76
- package/dist/protocols/cbor/value.d.ts +0 -10
- package/dist/protocols/cbor/value.js +2 -2
- package/dist/protocols/dictionary.yaml +1 -1
- package/dist/protocols/index.d.ts +3 -5
- package/dist/protocols/index.js +5 -7
- package/dist/protocols/packet.d.ts +0 -3
- package/dist/protocols/packet.js +0 -6
- package/dist/protocols/plotGroups.js +1 -1
- package/dist/types.d.ts +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ npm install poe-svelte-ui-lib
|
|
|
61
61
|
|
|
62
62
|
**Битовые поля конфигурации** — `bitFieldGet(word, start, end)`/`bitFieldSet(word, start, end, value)`: поле `start..end` 32-битного слова `{GROUP}.Cfg` (Select/Input с `bitMode`) обычной арифметикой, без знаковых 32-битных операций JS — бит 31 разбирается и собирается корректно, результат всегда 0..2³²−1; тест против эталона на BigInt — `tests/bitField.test.mjs`.
|
|
63
63
|
|
|
64
|
-
**Ядро протоколов** — `poe-svelte-ui-lib/protocols` (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и SYP-DevTerminal CBOR-кодек (RFC 8949)
|
|
64
|
+
**Ядро протоколов** — `poe-svelte-ui-lib/protocols`, описание протокола — [`src/lib/protocols/PROTOCOL.md`](src/lib/protocols/PROTOCOL.md) (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и SYP-DevTerminal CBOR-кодек (RFC 8949); `valueToCbor`/`cborToValue` — обычные значения, двоичные данные как `Uint8Array` (WebSocket, хранилище, телеметрия); `jsonToCbor` — JSON-совместимое значение в CBOR, строки base64 под ключами с `BIN` — байты (поля ввода терминала и отладки). Пакет `poe_cbor_pkt` — indefinite-массив объектов `[[TrgBusID, SrcBusID, HAR, Payload, CRC16], …]` (CAN, RS485, UART-консоль), `Payload` — одно CBOR item (сырые данные — `encodePacketBytesPayload`, пустой массив — `null`): `encodePacket`/`decodePacket` (разбор с начала буфера — `complete` с длиной, `incomplete` для потока или `garbage`; объекты одному адресату не склеиваются, порядок сохраняется, неверный CRC — флаг `crcOk`), `BUS_ID_REGEXP`/`parseBusId`/`formatBusId` — поля BusID в HEX и вывод `0x0000`, `makeHar(header, argument, reply = true)`/`splitHar` — `HAR`: заголовок в битах 14-15, аргумент в битах 1-13 (0..8191), флаг ответа в бите 0 (как `HAR`/`HAR_NR` прошивки), `packetObjectCrc`; контрольная сумма `crc16Kermit` — строго побайтная, как в `common.c` прошивок. Байты совпадают с `poe_cbor_pkt.c` прошивки — проверка `npm run TestProtocols` (`tests/protocols.test.mjs`, эталонные пакеты собраны прошивочным кодом). Протокол xPOE-Debug (UART-консоль `system_console`) — тот же пакет `poe_cbor_pkt` вперемешку с текстом загрузчика: `encodeXPoeDebugFrame(header, argument, payload, srcId, trgId, reply)` кодирует запрос, `xPoeDebugHar` — HAR по именам словаря, `createXPoeDebugParser().push(байты)` разбирает поток пакетов (каждая запись — отдельный элемент) и внешний текст (`raw`), логи `OK! DebugLog` — `[уровень, тег, текст, FHS]` (`DEBUG_LOG_ARGUMENT`, FHS — в `freeHeap`). Строка таблицы — `xPoeDebugRow`, колонки — `xPoeDebugTableColumns` (основной вход библиотеки): общие для отладки облака и терминала. CAN/CAN FD через slcan-адаптер (`can_service` прошивки): `canFragments`/`encodeCanPacket` — нарезка пакета на кадры как `CAN_Send` (бит 10 ID — «последний», биты 0..9 — `packetId`, по 8 / 64 байта), `encodeSlcanFrame`, `createCanParser().push(байты)` — разбор кадров slcan со сборкой фрагментов и обрезкой паддинга CAN FD. RS485 — сырой поток пакетов: `createPacketStreamParser().push(байты)` (пауза обрывает недособранное, мусор пропускается по байту). Открытие slcan-адаптера — `slcanOpenCommands(скорость, скоростьДанных?)`. Поле Value команды xPOE-Debug из ввода — `xPoeDebugValueFromText` (hex как есть, JSON → CBOR, пусто — пустой Payload). Графики отладки — пакет `OK! DebugPlot` (`LOG_PLOT` прошивки, `DEBUG_PLOT_ARGUMENT`): `debugPlotValues(value)` — пары `[имя, число]` из HEX CBOR-карты `Payload`, `createPlotGroups().update(пары)` раскладывает по группам (префикс до точки) в порядке карты и возвращает обновлённые группы, `data(группа)` — последние значения. Словарь протокола — `HEADER_CODES`, `DEFAULT_ARGUMENT_CODES` (генерируется из мастер-словаря `src/lib/protocols/dictionary.yaml` — `npm run Dictionary`, перед упаковкой автоматически; сам файл поставляется как `poe-svelte-ui-lib/protocols/dictionary.yaml`, `.framework/Dictionary.yaml` прошивок — его справочные копии, сборка прошивок сверяет с ними `api_har.h`), `parseArgumentDictionary` (строки `- [Строка, Enum, Число]`), `nameByCode`. Своих копий протоколов и словаря в приложениях не держим: терминал и отладка облака читают и пишут порт сырыми байтами, разбор и сборка — здесь.
|
|
65
65
|
|
|
66
66
|
**Шапка смарт-виджета** (`WidgetHeader`): значок, заголовок и сворачивание слева, «?» в правом краю; правая часть (режимы, «Обновить» и т. п.) стоит в той же строке, а если не помещается — уходит второй строкой под заголовок и там переносится.
|
|
67
67
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
import { T } from "../locales/i18n"
|
|
3
3
|
import type { IFileAttachProps } from "../types"
|
|
4
4
|
import { twMerge } from "tailwind-merge"
|
|
5
|
+
import Button from "../Button/Button.svelte"
|
|
6
|
+
|
|
7
|
+
/* Иконка «открыть файл» для type "button" — документ со стрелкой вверх, контур 1.75 как у остальных кнопок */
|
|
8
|
+
const OPEN_FILE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/><path d="M12 18v-6"/><path d="m9 15 3-3 3 3"/></svg>`
|
|
5
9
|
|
|
6
10
|
let {
|
|
7
11
|
id = crypto.randomUUID(),
|
|
@@ -13,6 +17,8 @@
|
|
|
13
17
|
imageSize = { height: "10rem", width: "10rem", fitMode: "cover", form: "square" },
|
|
14
18
|
disabled = false,
|
|
15
19
|
currentImage = $bindable(""),
|
|
20
|
+
info,
|
|
21
|
+
icon,
|
|
16
22
|
onChange = () => {},
|
|
17
23
|
}: IFileAttachProps = $props()
|
|
18
24
|
|
|
@@ -45,6 +51,8 @@
|
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
onChange(event, file)
|
|
54
|
+
/* Кнопка «Открыть файл» — повторный выбор того же файла тоже должен срабатывать */
|
|
55
|
+
if (type === "button") input.value = ""
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
const triggerFileInput = () => {
|
|
@@ -60,7 +68,11 @@
|
|
|
60
68
|
<span class={twMerge(`w-full text-[12px] font-semibold text-(--muted-color)`, label.class)}>{label.name}</span>
|
|
61
69
|
{/if}
|
|
62
70
|
|
|
63
|
-
{#if type === "
|
|
71
|
+
{#if type === "button"}
|
|
72
|
+
<!-- Иконочная кнопка с подсказкой, как UI.Button; выбор файла — скрытый input за ней -->
|
|
73
|
+
<Button {componentClass} {disabled} content={{ icon: icon ?? OPEN_FILE_ICON, info }} onClick={triggerFileInput} />
|
|
74
|
+
<input id={ID} type="file" class="hidden" {accept} {disabled} onchange={handleFileChange} />
|
|
75
|
+
{:else if type === "image"}
|
|
64
76
|
<div class="relative">
|
|
65
77
|
<button
|
|
66
78
|
class="flex items-center justify-center overflow-hidden {imageSize.form === 'circle' ? 'rounded-full' : 'rounded-[12px]'}
|
|
@@ -17,6 +17,18 @@
|
|
|
17
17
|
|
|
18
18
|
## Особенности
|
|
19
19
|
|
|
20
|
+
- `type="button"` — иконочная кнопка «Открыть файл» (скрытый `input` за `UI.Button`) с подсказкой `info`, для панелей инструментов: открыть файл для редактирования рядом с кнопками «Сохранить в файл», «Загрузить с сервера»; сохранение в файл — обычная `UI.Button`
|
|
21
|
+
|
|
22
|
+
```svelte
|
|
23
|
+
<UI.FileAttach
|
|
24
|
+
type="button"
|
|
25
|
+
accept=".yaml,.gz"
|
|
26
|
+
componentClass={TOOL_BUTTON}
|
|
27
|
+
info={{ text: "Открыть файл", side: "top" }}
|
|
28
|
+
onChange={(event, file) => open(file)}
|
|
29
|
+
/>
|
|
30
|
+
```
|
|
31
|
+
|
|
20
32
|
- Для списка файлов, удаления и прогресса закачки используйте виджет «Файловая система»
|
|
21
33
|
|
|
22
34
|
### Адаптивность
|
|
@@ -35,24 +47,26 @@
|
|
|
35
47
|
|
|
36
48
|
## Пропсы
|
|
37
49
|
|
|
38
|
-
| Название | Тип | По умолчанию | Описание
|
|
39
|
-
| ------------------- | -------------------------------------------- | --------------------- |
|
|
40
|
-
| `id` | `string` | `crypto.randomUUID()` | Уникальный идентификатор компонента
|
|
41
|
-
| `wrapperClass` | `string` | `""` | Дополнительные CSS-классы для обертки компонента
|
|
42
|
-
| `componentClass` | `string` | `""` | Дополнительные CSS-классы для элемента с именем выбранного файла
|
|
43
|
-
| `label` | `object` | `-` | Настройки заголовка компонента
|
|
44
|
-
| `label.name` | `string` | `""` | Текст заголовка компонента
|
|
45
|
-
| `label.class` | `string` | `""` | CSS-классы для стилизации текста заголовка
|
|
46
|
-
| `type` | `"file" \| "image"`
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `imageSize
|
|
51
|
-
| `imageSize.
|
|
52
|
-
| `imageSize.
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
50
|
+
| Название | Тип | По умолчанию | Описание |
|
|
51
|
+
| ------------------- | -------------------------------------------- | --------------------- | ---------------------------------------------------------------------------------- |
|
|
52
|
+
| `id` | `string` | `crypto.randomUUID()` | Уникальный идентификатор компонента |
|
|
53
|
+
| `wrapperClass` | `string` | `""` | Дополнительные CSS-классы для обертки компонента |
|
|
54
|
+
| `componentClass` | `string` | `""` | Дополнительные CSS-классы для элемента с именем выбранного файла |
|
|
55
|
+
| `label` | `object` | `-` | Настройки заголовка компонента |
|
|
56
|
+
| `label.name` | `string` | `""` | Текст заголовка компонента |
|
|
57
|
+
| `label.class` | `string` | `""` | CSS-классы для стилизации текста заголовка |
|
|
58
|
+
| `type` | `"file" \| "image" \| "button"` | `"file"` | Тип компонента: поле выбора файла, изображение или иконочная кнопка «Открыть файл» |
|
|
59
|
+
| `info` | `{ text, side }` | `-` | Только `button`: всплывающая подсказка, как у `UI.Button` |
|
|
60
|
+
| `icon` | `Component \| string` | документ со стрелкой | Только `button`: своя иконка (компонент или SVG-строка) |
|
|
61
|
+
| `accept` | `string` | `"*/*"` | MIME-типы файлов, которые можно выбрать |
|
|
62
|
+
| `imageSize` | `object` | `-` | Настройки отображения изображения |
|
|
63
|
+
| `imageSize.height` | `string` | `"10rem"` | Высота области отображения изображения |
|
|
64
|
+
| `imageSize.width` | `string` | `"10rem"` | Ширина области отображения изображения |
|
|
65
|
+
| `imageSize.fitMode` | `"cover" \| "contain"` | `"cover"` | Режим масштабирования изображения |
|
|
66
|
+
| `imageSize.form` | `"square" \| "circle"` | `"square"` | Форма области отображения изображения |
|
|
67
|
+
| `disabled` | `boolean` | `false` | Состояние отключения компонента |
|
|
68
|
+
| `currentImage` | `string \| null` | `""` | Текущее изображение (для связывания) |
|
|
69
|
+
| `onChange` | `(event: Event, file: File \| null) => void` | `() => {}` | Обработчик изменения файла |
|
|
56
70
|
|
|
57
71
|
### Колбэки
|
|
58
72
|
|
|
@@ -70,6 +70,15 @@
|
|
|
70
70
|
let boards: Board[] = $state([])
|
|
71
71
|
let now = $state(Date.now())
|
|
72
72
|
let mapInstance: any = $state()
|
|
73
|
+
/* Вид карты (центр, масштаб, поворот) переживает сворачивание: тело виджета пересоздаёт карту, и она открывается там, где её оставили */
|
|
74
|
+
let view = $state.raw({ center: { lat: 30, lng: 0 }, zoom: 1.5, bearing: 0 })
|
|
75
|
+
$effect(() => {
|
|
76
|
+
const map = mapInstance
|
|
77
|
+
if (!map) return
|
|
78
|
+
const save = () => (view = { center: map.getCenter(), zoom: map.getZoom(), bearing: map.getBearing() })
|
|
79
|
+
map.on("moveend", save)
|
|
80
|
+
return () => map.off("moveend", save)
|
|
81
|
+
})
|
|
73
82
|
let isCopied = $state(false)
|
|
74
83
|
let isDarkMode = $state(false)
|
|
75
84
|
|
|
@@ -211,8 +220,9 @@
|
|
|
211
220
|
bind:map={mapInstance}
|
|
212
221
|
class="h-full w-full overflow-hidden rounded-[14px] border border-(--hairline-color)"
|
|
213
222
|
style={isDarkMode ? "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json" : "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"}
|
|
214
|
-
zoom={
|
|
215
|
-
center={
|
|
223
|
+
zoom={view.zoom}
|
|
224
|
+
center={view.center}
|
|
225
|
+
bearing={view.bearing}
|
|
216
226
|
maxPitch={0}
|
|
217
227
|
>
|
|
218
228
|
<NavigationControl />
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onDestroy } from "svelte"
|
|
2
|
+
import { onDestroy, untrack } from "svelte"
|
|
3
3
|
import { getMapContext } from "./contexts.svelte.js"
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
@@ -23,31 +23,36 @@
|
|
|
23
23
|
properties: {},
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
+
/* Источник и слой — один раз; пока стиль карты грузится, добавление ждёт в очереди waitForStyleLoaded */
|
|
26
27
|
$effect(() => {
|
|
27
28
|
if (!mapCtx.map || added) return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
untrack(() => {
|
|
30
|
+
mapCtx.addSource(id, { type: "geojson", data: toGeoJSON() })
|
|
31
|
+
mapCtx.addLayer({
|
|
32
|
+
id,
|
|
33
|
+
type: "line",
|
|
34
|
+
source: id,
|
|
35
|
+
layout: { "line-join": "round", "line-cap": "round" },
|
|
36
|
+
paint: { "line-color": color, "line-width": width, "line-opacity": opacity },
|
|
37
|
+
})
|
|
36
38
|
})
|
|
37
39
|
added = true
|
|
38
40
|
})
|
|
39
41
|
|
|
42
|
+
/* Координаты и оформление читаются до любых проверок — иначе эффект не подписывается на них и, запущенный до загрузки стиля, больше не срабатывает */
|
|
40
43
|
$effect(() => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
const data = toGeoJSON()
|
|
45
|
+
if (!added) return
|
|
46
|
+
mapCtx.waitForStyleLoaded((map) => (map.getSource(id) as any)?.setData(data))
|
|
44
47
|
})
|
|
45
48
|
|
|
46
49
|
$effect(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
mapCtx.map
|
|
50
|
-
|
|
50
|
+
const paint = { "line-color": color, "line-width": width, "line-opacity": opacity }
|
|
51
|
+
if (!added) return
|
|
52
|
+
mapCtx.waitForStyleLoaded((map) => {
|
|
53
|
+
if (!map.getLayer(id)) return
|
|
54
|
+
for (const [name, value] of Object.entries(paint)) map.setPaintProperty(id, name, value)
|
|
55
|
+
})
|
|
51
56
|
})
|
|
52
57
|
|
|
53
58
|
onDestroy(() => {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
style?: any | string
|
|
10
10
|
zoom?: number
|
|
11
11
|
center?: any
|
|
12
|
+
bearing?: number
|
|
12
13
|
maxPitch?: number
|
|
13
14
|
map?: any
|
|
14
15
|
children?: Snippet<[any]>
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
style = { version: 8, sources: {}, layers: [] },
|
|
20
21
|
zoom = $bindable(undefined),
|
|
21
22
|
center = $bindable(undefined),
|
|
23
|
+
bearing = 0,
|
|
22
24
|
maxPitch = 60,
|
|
23
25
|
map = $bindable(undefined),
|
|
24
26
|
children,
|
|
@@ -45,13 +47,18 @@
|
|
|
45
47
|
target.on("sourcedata", tryCollapse)
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
/* Стиль, с которым создана или на который переключена карта — setStyle только при его реальной смене */
|
|
51
|
+
let appliedStyle: any = undefined
|
|
52
|
+
|
|
48
53
|
const createMap = () => {
|
|
49
54
|
if (!window.maplibregl || map || !container) return
|
|
55
|
+
appliedStyle = style
|
|
50
56
|
map = new window.maplibregl.Map({
|
|
51
57
|
container,
|
|
52
58
|
style,
|
|
53
59
|
zoom,
|
|
54
60
|
center,
|
|
61
|
+
bearing,
|
|
55
62
|
maxPitch,
|
|
56
63
|
attributionControl: { compact: true },
|
|
57
64
|
})
|
|
@@ -63,7 +70,8 @@
|
|
|
63
70
|
$effect(createMap)
|
|
64
71
|
|
|
65
72
|
$effect(() => {
|
|
66
|
-
if (map && style) {
|
|
73
|
+
if (map && style && style !== appliedStyle) {
|
|
74
|
+
appliedStyle = style
|
|
67
75
|
mapCtx.setStyle(style)
|
|
68
76
|
}
|
|
69
77
|
})
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
{ "Name": "Internal", "Total": 327680, "Free": 118000, "MinFree": 92000 },
|
|
20
20
|
{ "Name": "PSRAM", "Total": 4194304, "Free": 3980000, "MinFree": 3920000 }
|
|
21
21
|
],
|
|
22
|
-
"Tasks": [{ "Name": "
|
|
22
|
+
"Tasks": [{ "Name": "api_rx_task", "FreeMin": 1240, "Prio": 5, "Core": 1, "State": 2 }]
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# 📡 Протокол POE на базе CBOR
|
|
2
|
+
|
|
3
|
+
Один бинарный пакет `poe_cbor_pkt` на всех проводах: CAN, CAN FD, RS485, LoRa и UART-консоль (xPOE-Debug). Отдельный конверт — только у WebSocket облака.
|
|
4
|
+
Реализации: прошивки ProdFactory-ESP / ProdFactory-STM (`components/core/poe_cbor`, `api_service`) и ядро `poe-svelte-ui-lib/protocols` (DevCloud, SYP-DevTerminal, WebApp).
|
|
5
|
+
|
|
6
|
+
## 📦 Пакет
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
0x9F [TrgBusID, SrcBusID, HAR, Payload, CRC16] [ … ] 0xFF
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
- Пакет — CBOR-массив неопределённой длины (`0x9F` … `0xFF`), записей сколько угодно: разным адресатам или несколько одному, порядок сохраняется
|
|
13
|
+
- Запись — массив ровно из 5 элементов (`0x85`); `0x9F 0x85` — маркер начала пакета в потоке
|
|
14
|
+
- Числа — минимальной длиной CBOR: меньше 24 — 1 байт, до 255 — 2, до 65535 — 3
|
|
15
|
+
|
|
16
|
+
| Поле | Тип | Смысл |
|
|
17
|
+
| ---------- | --------- | ----------------------------------------------------------------------------------- |
|
|
18
|
+
| `TrgBusID` | uint16 | Адресат: BusID модуля, `0xFFFF` — всем |
|
|
19
|
+
| `SrcBusID` | uint16 | Отправитель, ответ уходит ему; `0x0000` — внешний клиент (терминал, отладка облака) |
|
|
20
|
+
| `HAR` | uint16 | Заголовок, аргумент и флаг ответа |
|
|
21
|
+
| `Payload` | CBOR item | Тело команды |
|
|
22
|
+
| `CRC16` | uint16 | CRC-16/KERMIT записи |
|
|
23
|
+
|
|
24
|
+
## 🔢 HAR
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
бит 15-14 hdr 0 GET · 1 SET · 2 OK! · 3 ER!
|
|
28
|
+
бит 13-1 arg код из словаря, 0..8191
|
|
29
|
+
бит 0 reply 1 — получатель отвечает
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Прошивка: `har_t` (`api_har.h`), `HAR(h, a)` — с ответом, `HAR_NR(h, a)` — без; библиотека: `makeHar` / `splitHar`
|
|
33
|
+
- Ответ — только на запрос с `reply = 1`, сам ответ всегда без флага; телеметрия и потоки — `HAR_NR`
|
|
34
|
+
- Строки (`"GET"`, `"ModCfg"`) по проводу не передаются; словарь один — `dictionary.yaml` этой библиотеки, в прошивках копия `.framework/Dictionary.yaml`, сборка сверяет с ней `api_har.h`
|
|
35
|
+
- Всё отладочное в словаре начинается с `Debug`: `DebugLog` = 11, `DebugPlot` = 12
|
|
36
|
+
|
|
37
|
+
## 📄 Payload — всегда одно CBOR item
|
|
38
|
+
|
|
39
|
+
| Данные | Кодирование |
|
|
40
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
41
|
+
| Карта, массив, число, строка | Как есть (ответы `Writer_*`, `{"CfgGroup":"CFG"}`) |
|
|
42
|
+
| Сырые бинарные поля | Байтовая строка `h'…'`: `CBOR_PACK` / `CBOR_EncodeFields`, разбор — `CBOR_UNPACK` (снимает байтовую строку и сверяет длину с суммой полей) |
|
|
43
|
+
| Пустое тело | `null` (`0xF6`), в `pkt->data` — `data_len = 0` |
|
|
44
|
+
|
|
45
|
+
Флагов формата нет — тип виден по самому CBOR.
|
|
46
|
+
|
|
47
|
+
## ✅ CRC-16/KERMIT
|
|
48
|
+
|
|
49
|
+
- Полином `0x8408` (отражённый `0x1021`), init 0, `check("123456789") = 0x2189`
|
|
50
|
+
- Считается по CBOR-байтам записи от `0x85` до конца `Payload`, поле CRC не входит — покрывает адреса и флаг ответа
|
|
51
|
+
- Запись с неверным CRC пропускается, остальные записи пакета принимаются
|
|
52
|
+
|
|
53
|
+
## 🧭 Адресация
|
|
54
|
+
|
|
55
|
+
- BusID модуля — CRC16 серийного номера (`get_dev_bus_id`), `0x0000` и `0xFFFF` модулю не выдаются
|
|
56
|
+
- Шина: модуль принимает записи со своим `TrgBusID` и `0xFFFF`, собственное эхо (CAN loopback, RS485) отбрасывает по `SrcBusID`
|
|
57
|
+
- Консоль: `0x0000`, свой BusID и `0xFFFF` — этот модуль, BusID модуля из `ModList` — головной модуль пересылает команду по `ModSN` и возвращает ответ
|
|
58
|
+
- Без обработчика: `OK!`/`ER!` и широковещательные — молча, адресованные `GET`/`SET` — в лог
|
|
59
|
+
|
|
60
|
+
## 🚌 Транспорты
|
|
61
|
+
|
|
62
|
+
| Канал | Передача |
|
|
63
|
+
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
64
|
+
| CAN / CAN FD | Кадры по 8 / 64 байта; 11-битный ID: бит 10 — последний фрагмент, биты 9-0 — номер пакета; паддинг CAN FD отрезается по длине пакета |
|
|
65
|
+
| RS485 | Сырой поток: начало `0x9F 0x85`, пауза приёма (20 мс) сбрасывает недособранный хвост, мусор сдвигается по байту |
|
|
66
|
+
| LoRa | Пакет целиком в радиокадре |
|
|
67
|
+
| UART-консоль | Сырой поток вперемешку с текстом загрузчика: начало `0x9F 0x85`, остальное — текст (RAW), хвост сбрасывается после 100 мс тишины |
|
|
68
|
+
|
|
69
|
+
## 🐞 Консоль: отладочные пакеты
|
|
70
|
+
|
|
71
|
+
Прошивка шлёт их с `TrgBusID = 0`, `SrcBusID` = свой BusID, без флага ответа.
|
|
72
|
+
|
|
73
|
+
| HAR | Payload |
|
|
74
|
+
| --------------- | ------------------------------------------------------------ |
|
|
75
|
+
| `OK! DebugLog` | `[уровень, тег, текст или байты, FHS]`, FHS — свободная куча |
|
|
76
|
+
| `OK! DebugPlot` | `{"Группа.Имя": float32, …}` — один вызов `LOG_PLOT` |
|
|
77
|
+
|
|
78
|
+
Ответ API в консоль — голый `Payload`; непустой `Message` приходит отдельным `DebugLog`.
|
|
79
|
+
|
|
80
|
+
## 🌐 WebSocket (облако и локальный веб)
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
запрос {HDR, ARG, VAL: {DevSN, ModSN?, TrgBusID?, SrcBusID?, Payload?}}
|
|
84
|
+
ответ {HDR, ARG, VAL: {DevSN, ModSN, Message, Payload}}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`DevSN` обязателен и должен совпасть с устройством, `ModSN` — адресат (без него — головной модуль). Приём — `API_Service_ReceiveWsPacket`, ответ — `API_Service_BeginResponse`; ответ модуля с шины головной модуль оборачивает в конверт сам.
|
|
88
|
+
|
|
89
|
+
## 💡 Пример: `GET ModCfg {}` из терминала
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
9F 85 00 00 18 49 A0 19 66 06 FF
|
|
93
|
+
│ │ │ │ │ │ │ └ конец пакета
|
|
94
|
+
│ │ │ │ │ │ └ CRC16 = 0x6606
|
|
95
|
+
│ │ │ │ │ └ Payload = {}
|
|
96
|
+
│ │ │ │ └ HAR = 0x0049: GET, arg 36 (ModCfg), reply = 1
|
|
97
|
+
│ │ │ └ SrcBusID = 0x0000 (терминал)
|
|
98
|
+
│ │ └ TrgBusID = 0x0000 (модуль консоли)
|
|
99
|
+
│ └ запись из 5 полей
|
|
100
|
+
└ начало пакета
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 🧩 API
|
|
104
|
+
|
|
105
|
+
| | Прошивка (ESP и STM) | `poe-svelte-ui-lib/protocols` |
|
|
106
|
+
| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
|
|
107
|
+
| Пакет | `CBOR_PktEncInit` / `CBOR_PktEncAddRaw` / `CBOR_PACK` / `CBOR_PktEncFinish`, `CBOR_PktDecInit` / `CBOR_PktDecNext`, поток — `CBOR_PktStreamPush` | `encodePacket`, `decodePacket`, `createPacketStreamParser`, `encodePacketBytesPayload` |
|
|
108
|
+
| HAR | `har_t`, `HAR`, `HAR_NR`, `API_Service_HarName`, `API_Service_HarByName` | `makeHar`, `splitHar`, `xPoeDebugHar`, `HEADER_CODES`, `DEFAULT_ARGUMENT_CODES` |
|
|
109
|
+
| Приём | `API_Service_ReceiveBusPacket`, `API_Service_ReceiveWsPacket`, консоль — `system_console` | `createXPoeDebugParser`, `createCanParser` |
|
|
110
|
+
| Ответ | `API_Service_BeginResponse`, `Writer_*`, `API_Service_FinishResponse` | — |
|
|
111
|
+
| Консоль | `LOG_*`, `LOG_HEX`, `LOG_PLOT` | `encodeXPoeDebugFrame`, `xPoeDebugRow`, `xPoeDebugTableColumns`, `debugPlotValues` |
|
|
112
|
+
| CAN | `can_service` | `encodeCanPacket`, `createCanParser`, `slcanOpenCommands` |
|
|
113
|
+
| Тесты | хостовые `tests/` прошивок | `npm run TestProtocols` по эталонным байтам прошивки |
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
import { type CborValue } from "./types.js";
|
|
2
|
-
export declare class CborToJson {
|
|
3
|
-
static convert(value: CborValue): unknown;
|
|
4
|
-
static decode(buffer: ArrayBuffer | Uint8Array): unknown;
|
|
5
|
-
private static keyToString;
|
|
6
|
-
}
|
|
7
|
-
export declare class JsonToCbor {
|
|
8
|
-
static convert(value: unknown, parentKey?: string): CborValue;
|
|
9
|
-
static encode(value: unknown): Uint8Array;
|
|
10
|
-
}
|
|
11
|
-
export declare function cborToJson(buffer: ArrayBuffer | Uint8Array): unknown;
|
|
12
1
|
export declare function jsonToCbor(value: unknown): Uint8Array;
|
|
@@ -1,77 +1,9 @@
|
|
|
1
|
-
/*
|
|
2
|
-
import {
|
|
3
|
-
import { decodeCbor } from "./decoder.js";
|
|
1
|
+
/* JSON-совместимое значение → CBOR (поля ввода терминала и отладки): строки base64 под ключами с "BIN" — байты */
|
|
2
|
+
import {} from "./types.js";
|
|
4
3
|
import { encodeCbor } from "./encoder.js";
|
|
5
|
-
import { base64ToBytes
|
|
6
|
-
/* Класс для конвертации CBOR в JSON */
|
|
7
|
-
export class CborToJson {
|
|
8
|
-
/* Преобразовать CBOR значение в JSON-совместимый объект */
|
|
9
|
-
static convert(value) {
|
|
10
|
-
/* null */
|
|
11
|
-
if (value === null)
|
|
12
|
-
return null;
|
|
13
|
-
/* undefined */
|
|
14
|
-
if (value === undefined)
|
|
15
|
-
return null;
|
|
16
|
-
/* boolean */
|
|
17
|
-
if (typeof value === "boolean")
|
|
18
|
-
return value;
|
|
19
|
-
/* number */
|
|
20
|
-
if (typeof value === "number")
|
|
21
|
-
return value;
|
|
22
|
-
/* string */
|
|
23
|
-
if (typeof value === "string")
|
|
24
|
-
return value;
|
|
25
|
-
/* Uint8Array -> Base64 строка */
|
|
26
|
-
if (value instanceof Uint8Array)
|
|
27
|
-
return bytesToBase64(value);
|
|
28
|
-
/* Array */
|
|
29
|
-
if (Array.isArray(value))
|
|
30
|
-
return value.map((item) => this.convert(item));
|
|
31
|
-
/* bigint */
|
|
32
|
-
if (typeof value === "bigint") {
|
|
33
|
-
/* Если в пределах safe integer - возвращаем как число */
|
|
34
|
-
if (value >= BigInt(Number.MIN_SAFE_INTEGER) && value <= BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
35
|
-
return Number(value);
|
|
36
|
-
}
|
|
37
|
-
/* Иначе возвращаем как строку */
|
|
38
|
-
return value.toString();
|
|
39
|
-
}
|
|
40
|
-
/* Map -> Object (check native Map) */
|
|
41
|
-
if (value instanceof globalThis.Map) {
|
|
42
|
-
const obj = {};
|
|
43
|
-
for (const [key, val] of value) {
|
|
44
|
-
/* Ключи приводим к строкам */
|
|
45
|
-
const keyStr = this.keyToString(key);
|
|
46
|
-
obj[keyStr] = this.convert(val);
|
|
47
|
-
}
|
|
48
|
-
return obj;
|
|
49
|
-
}
|
|
50
|
-
/* CborTag - распаковываем в объект с полем _tag */
|
|
51
|
-
if (value instanceof CborTag)
|
|
52
|
-
return { _tag: Number(value.tag), value: this.convert(value.value) };
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
/* CBOR -> JSON объект */
|
|
56
|
-
static decode(buffer) {
|
|
57
|
-
const value = decodeCbor(buffer);
|
|
58
|
-
return this.convert(value);
|
|
59
|
-
}
|
|
60
|
-
/* Преобразовать ключ в строку */
|
|
61
|
-
static keyToString(key) {
|
|
62
|
-
if (typeof key === "string")
|
|
63
|
-
return key;
|
|
64
|
-
if (typeof key === "number")
|
|
65
|
-
return key.toString();
|
|
66
|
-
if (typeof key === "bigint")
|
|
67
|
-
return key.toString();
|
|
68
|
-
if (typeof key === "boolean")
|
|
69
|
-
return key.toString();
|
|
70
|
-
return JSON.stringify(key);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
4
|
+
import { base64ToBytes } from "./base64.js";
|
|
73
5
|
/* Класс для конвертации JSON в CBOR */
|
|
74
|
-
|
|
6
|
+
class JsonToCbor {
|
|
75
7
|
/* Преобразовать JSON-совместимое значение в CBOR */
|
|
76
8
|
static convert(value, parentKey) {
|
|
77
9
|
/* null */
|
|
@@ -120,10 +52,6 @@ export class JsonToCbor {
|
|
|
120
52
|
}
|
|
121
53
|
}
|
|
122
54
|
/* Упрощённые функции для быстрого использования */
|
|
123
|
-
/* Декодировать CBOR в JSON объект */
|
|
124
|
-
export function cborToJson(buffer) {
|
|
125
|
-
return CborToJson.decode(buffer);
|
|
126
|
-
}
|
|
127
55
|
/* Закодировать JSON в CBOR */
|
|
128
56
|
export function jsonToCbor(value) {
|
|
129
57
|
return JsonToCbor.encode(value);
|
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
import { type CborValue } from "./types.js";
|
|
2
|
-
export declare class CborToValue {
|
|
3
|
-
static convert(value: CborValue): unknown;
|
|
4
|
-
static decode(buffer: ArrayBuffer | Uint8Array): unknown;
|
|
5
|
-
private static keyToString;
|
|
6
|
-
}
|
|
7
|
-
export declare class ValueToCbor {
|
|
8
|
-
static convert(value: unknown): CborValue;
|
|
9
|
-
static encode(value: unknown): Uint8Array;
|
|
10
|
-
}
|
|
11
1
|
export declare function cborToValue(buffer: ArrayBuffer | Uint8Array): unknown;
|
|
12
2
|
export declare function valueToCbor(value: unknown): Uint8Array;
|
|
@@ -3,7 +3,7 @@ import { CborTag } from "./types.js";
|
|
|
3
3
|
import { decodeCbor } from "./decoder.js";
|
|
4
4
|
import { encodeCbor } from "./encoder.js";
|
|
5
5
|
/* Класс для конвертации CBOR в обычное JS-значение */
|
|
6
|
-
|
|
6
|
+
class CborToValue {
|
|
7
7
|
/* Преобразовать CBOR значение в обычный JS-объект/значение */
|
|
8
8
|
static convert(value) {
|
|
9
9
|
/* null */
|
|
@@ -70,7 +70,7 @@ export class CborToValue {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
/* Класс для конвертации обычного JS-значения в CBOR */
|
|
73
|
-
|
|
73
|
+
class ValueToCbor {
|
|
74
74
|
/* Преобразовать обычное JS-значение в CBOR */
|
|
75
75
|
static convert(value) {
|
|
76
76
|
/* null */
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- [BackUp, A_BACKUP, 9] # Резервная копия конфигурации
|
|
17
17
|
- [BindMKey, A_BIND_MOD_KEY, 10] # SET: привязка ключа модуля (ModBindKey)
|
|
18
18
|
- [DebugLog, A_DEBUG_LOG, 11] # OK!: сообщение отладки — CBOR-массив [уровень, тег, текст|байты, FHS]
|
|
19
|
-
- [DebugPlot, A_DEBUG_PLOT, 12] # OK!: значения графиков отладки — CBOR-карта {"Имя": float32} (
|
|
19
|
+
- [DebugPlot, A_DEBUG_PLOT, 12] # OK!: значения графиков отладки — CBOR-карта {"Имя": float32} (LOG_PLOT)
|
|
20
20
|
- [DataStream, A_DATA_STREAM, 15] # OK!: поток данных групп конфигурации (CS_PushGroupStream)
|
|
21
21
|
|
|
22
22
|
# Локальная сеть
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
export * from "./cbor/types.js";
|
|
2
|
-
export { CborDecoder, decodeCbor } from "./cbor/decoder.js";
|
|
3
|
-
export { CborEncoder, encodeCbor } from "./cbor/encoder.js";
|
|
4
2
|
export { bytesToBase64, base64ToBytes } from "./cbor/base64.js";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
3
|
+
export { cborToValue, valueToCbor } from "./cbor/value.js";
|
|
4
|
+
export { jsonToCbor } from "./cbor/json.js";
|
|
7
5
|
export { crc16Kermit } from "./crc.js";
|
|
8
|
-
export {
|
|
6
|
+
export { BUS_ID_REGEXP, parseBusId, formatBusId, makeHar, splitHar, packetObjectCrc, encodePacketBytesPayload, encodePacket, decodePacket, createPacketStreamParser, type PacketObject, type DecodedPacketObject, type PacketParse, } from "./packet.js";
|
|
9
7
|
export { createPlotGroups, debugPlotValues, DEBUG_PLOT_ARGUMENT, type PlotPoint } from "./plotGroups.js";
|
|
10
8
|
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugHar, xPoeDebugValueFromText, xPoeDebugRow, DEBUG_LOG_ARGUMENT, type XPoeDebugItem, type XPoeDebugRow, } from "./xPoeDebug.js";
|
|
11
9
|
export { formatXPoeDebugValue, formatXPoeDebugValueForTable, formatXPoeDebugModal, bindXPoeDebugCopyButtons } from "./xPoeDebugView.js";
|
package/dist/protocols/index.js
CHANGED
|
@@ -2,17 +2,15 @@
|
|
|
2
2
|
/* Общее ядро протоколов обмена с устройствами для DevCloud, WebApp и SYP-DevTerminal — без Svelte, точка входа "poe-svelte-ui-lib/protocols"
|
|
3
3
|
(условие default: работает и в браузере, и под Node без Vite); импорты с расширением .js, чтобы их разрешал сам Node */
|
|
4
4
|
export * from "./cbor/types.js";
|
|
5
|
-
export { CborDecoder, decodeCbor } from "./cbor/decoder.js";
|
|
6
|
-
export { CborEncoder, encodeCbor } from "./cbor/encoder.js";
|
|
7
5
|
export { bytesToBase64, base64ToBytes } from "./cbor/base64.js";
|
|
8
6
|
/* Значение ↔ CBOR: двоичные данные — Uint8Array как есть (WebSocket, хранилище, телеметрия) */
|
|
9
|
-
export {
|
|
10
|
-
/* JSON
|
|
11
|
-
export {
|
|
7
|
+
export { cborToValue, valueToCbor } from "./cbor/value.js";
|
|
8
|
+
/* JSON-значение → CBOR: строки base64 под ключами с "BIN" кодируются байтами (поля ввода терминала и отладки) */
|
|
9
|
+
export { jsonToCbor } from "./cbor/json.js";
|
|
12
10
|
/* Контрольная сумма и пакет poe_cbor_pkt [[TrgBusID, SrcBusID, HAR, Payload, CRC16], …] — все шины и консоль */
|
|
13
11
|
export { crc16Kermit } from "./crc.js";
|
|
14
|
-
export {
|
|
15
|
-
/* Графики отладки: пакет OK! DebugPlot (
|
|
12
|
+
export { BUS_ID_REGEXP, parseBusId, formatBusId, makeHar, splitHar, packetObjectCrc, encodePacketBytesPayload, encodePacket, decodePacket, createPacketStreamParser, } from "./packet.js";
|
|
13
|
+
/* Графики отладки: пакет OK! DebugPlot (LOG_PLOT прошивки) — значения из CBOR-карты, график на группу, серии в порядке устройства */
|
|
16
14
|
export { createPlotGroups, debugPlotValues, DEBUG_PLOT_ARGUMENT } from "./plotGroups.js";
|
|
17
15
|
/* xPOE-Debug — UART-консоль как шина: пакеты вперемешку с текстом загрузчика, логи OK! DebugLog */
|
|
18
16
|
export { encodeXPoeDebugFrame, createXPoeDebugParser, xPoeDebugHar, xPoeDebugValueFromText, xPoeDebugRow, DEBUG_LOG_ARGUMENT, } from "./xPoeDebug.js";
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export declare const BROADCAST_BUS_ID = 65535;
|
|
2
|
-
export declare const EXTERNAL_BUS_ID = 0;
|
|
3
1
|
export declare const BUS_ID_REGEXP: RegExp;
|
|
4
2
|
export declare const parseBusId: (text: string | undefined, fallback?: number) => number;
|
|
5
3
|
export declare const formatBusId: (id: number) => string;
|
|
@@ -35,7 +33,6 @@ export type PacketParse = {
|
|
|
35
33
|
status: "garbage";
|
|
36
34
|
};
|
|
37
35
|
export declare const decodePacket: (bytes: Uint8Array) => PacketParse;
|
|
38
|
-
export declare const objectsFor: (objects: DecodedPacketObject[], busID: number) => DecodedPacketObject[];
|
|
39
36
|
export declare const createPacketStreamParser: ({ gapMs, maxBytes }?: {
|
|
40
37
|
gapMs?: number | undefined;
|
|
41
38
|
maxBytes?: number | undefined;
|
package/dist/protocols/packet.js
CHANGED
|
@@ -5,10 +5,6 @@ import { CborDecoder } from "./cbor/decoder.js";
|
|
|
5
5
|
import { CborEncoder } from "./cbor/encoder.js";
|
|
6
6
|
import { CborEndOfDataError } from "./cbor/types.js";
|
|
7
7
|
import { crc16Kermit } from "./crc.js";
|
|
8
|
-
/* Адрес «всем устройствам шины» — только адресат */
|
|
9
|
-
export const BROADCAST_BUS_ID = 0xffff;
|
|
10
|
-
/* Адрес внешнего клиента (терминал, отладка облака) — у модулей такого адреса не бывает */
|
|
11
|
-
export const EXTERNAL_BUS_ID = 0x0000;
|
|
12
8
|
/* BusID в полях ввода — HEX без "0x", 1-4 знака (uint16) */
|
|
13
9
|
export const BUS_ID_REGEXP = /^[0-9A-Fa-f]{1,4}$/;
|
|
14
10
|
/* HEX-поле BusID → число; пусто или не HEX — fallback */
|
|
@@ -132,8 +128,6 @@ export const decodePacket = (bytes) => {
|
|
|
132
128
|
}
|
|
133
129
|
return { status: "complete", length: offset, objects };
|
|
134
130
|
};
|
|
135
|
-
/* Объекты пакета для своего адреса (и broadcast) с верным CRC, по порядку — как CBOR_PktDecNext прошивки */
|
|
136
|
-
export const objectsFor = (objects, busID) => objects.filter((object) => object.crcOk && (object.trgBusID === busID || object.trgBusID === BROADCAST_BUS_ID));
|
|
137
131
|
/* Потоковый разбор пакетов из сырого потока (RS485): пауза дольше gapMs обрывает недособранный пакет, мусор сдвигается по байту,
|
|
138
132
|
без единого целого пакета буфер не растёт дальше maxBytes */
|
|
139
133
|
export const createPacketStreamParser = ({ gapMs = 100, maxBytes = 4096 } = {}) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* $lib/protocols/plotGroups.ts */
|
|
2
|
-
/* Графики отладки: пакет xPOE-Debug OK! DebugPlot (
|
|
2
|
+
/* Графики отладки: пакет xPOE-Debug OK! DebugPlot (LOG_PLOT прошивки), Payload — CBOR-карта {"Группа.Имя": float32, …} в порядке вызова.
|
|
3
3
|
График на группу — префикс имени до первой точки, без префикса — общая группа ""; серии в порядке карты, у серий, которых в кадре нет, — после них */
|
|
4
4
|
import { cborToValue } from "./cbor/value.js";
|
|
5
5
|
import { xPoeDebugValueFromText } from "./xPoeDebug.js";
|
package/dist/types.d.ts
CHANGED
|
@@ -116,8 +116,13 @@ export interface IFileAttachProps {
|
|
|
116
116
|
name?: string;
|
|
117
117
|
class?: string;
|
|
118
118
|
};
|
|
119
|
-
type?: "file" | "image";
|
|
119
|
+
type?: "file" | "image" | "button";
|
|
120
120
|
accept?: string;
|
|
121
|
+
info?: {
|
|
122
|
+
text: string;
|
|
123
|
+
side: "top" | "bottom" | "left" | "right";
|
|
124
|
+
};
|
|
125
|
+
icon?: ConstructorOfATypedSvelteComponent | string | null;
|
|
121
126
|
imageSize?: {
|
|
122
127
|
height?: string;
|
|
123
128
|
width?: string;
|