poe-svelte-ui-lib 1.32.2 → 1.33.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.
@@ -3,55 +3,78 @@
3
3
  ## Описание
4
4
 
5
5
  Смарт-виджет файловой системы устройства: занятость памяти, список файлов с удалением и закачка
6
- нового файла с прогрессом. Работает с объектом `FSInfo` группы `CFG` ровно в том виде, в каком
7
- его отдаёт прошивка (`FS_WriteFullInfo`, ProdFactory-ESP), и с прогрессом закачки `UlProg`.
8
- Компонент ничего не знает про DeviceStore/WebSocket — только `value`/`uploadProgress` и колбэки
9
- `onRefresh`/`onDelete`/`onUpload`, вся отправка (в том числе протокол закачки `FUpS` → `FUpP` →
10
- `FUpD`) на стороне вызывающего кода.
11
-
12
- Заменяет собой блок из отдельных компонентов (поля «Всего/Использовано/Свободно», кнопка
13
- `GET FSInfo`, таблица `FSList` с кнопкой `DelFile`, `FileAttach`, `ProgressBar` по `UlProg`) —
14
- одной карточкой в стиле `WidgetWiFi`/`WidgetDeviceInfo`.
15
-
16
- - **Занятость** — «занято X из Y», процент и полоса; от 75% полоса оранжевая, от 90% — красная.
17
- Размеры показываются в Б/КБ/МБ.
6
+ нового файла с прогрессом. Работает с одним объектом `CFG.FS` ровно в том виде, в каком его отдаёт
7
+ прошивка (`FS_WriteState`, ProdFactory-ESP), и с одной командой `FS`. Компонент ничего не знает про
8
+ DeviceStore/WebSocket — только `value` и колбэки `onRefresh`/`onDelete`/`onUpload`, вся отправка
9
+ (в том числе протокол закачки) на стороне вызывающего кода.
10
+
11
+ - **Занятость** — «занято X из Y», процент и полоса мастер-цвета; процент оранжевый от 75%,
12
+ красный от 90%. Размеры в Б/КБ/МБ.
18
13
  - **Файлы** — таблица «Путь / Размер / Удалить»; длинный путь обрезается «…», удаление
19
- подтверждается внутри карточки. Пустая ФС — «Файлов нет». Колонка удаления есть, только
20
- если передан `onDelete`.
14
+ подтверждается внутри карточки. Пустая ФС — «Файлов нет». Колонка удаления есть, только если
15
+ передан `onDelete`.
21
16
  - **Закачка** — блок есть, только если передан `onUpload`. Имя файла проверяется до отправки
22
- (`maxNameLength`, по умолчанию 31 — ограничение прошивки), пока `onUpload` не завершится,
23
- показывается прогресс из `uploadProgress`; ошибка из `onUpload` выводится под полем.
17
+ (`maxNameLength`, по умолчанию 31 — ограничение прошивки). Прогресс берётся из `value.Upload`
18
+ и виден, пока закачку ведёт этот клиент или пока устройство сообщает `Upload` (закачка с
19
+ другого клиента). Ошибка из `onUpload` выводится под полем.
20
+
21
+ ## Протокол устройства
22
+
23
+ Всё состояние ФС — один объект `CFG.FS`:
24
+
25
+ ```
26
+ "CFG.FS": {
27
+ "Total": 1441792, "Used": 98304, "Free": 1343488,
28
+ "List": [{ "Name": "/storage/cert.pem", "Size": 1834 }],
29
+ "Upload": { "Name": "fw.bin", "Size": 917504, "Progress": 35 }
30
+ }
31
+ ```
32
+
33
+ `Upload` есть, только пока идёт закачка. Снимок со списком `List` приходит на `GET ModCfg`,
34
+ `GET FS`, после удаления, после завершения или ошибки закачки и в рассылке `OK! Update`; ход
35
+ закачки — только `{"Upload": {...}}` (без списка, раз в 5%). Объект со списком заменяет состояние
36
+ целиком, без списка — дописывается поверх (это делает вызывающий код).
37
+
38
+ Команды — один аргумент `FS`, операция в поле `Op`; ответ всегда `FS` с тем же `Op`:
39
+
40
+ | Запрос | Ответ |
41
+ | ------------------------------------------------------------------------------------ | --------------------------------------------------------- |
42
+ | `GET FS` | `CFG.FS` — снимок |
43
+ | `SET FS {"Op": "Delete", "Name": "/storage/a.bin"}` | `Op`, `CFG.FS` — снимок |
44
+ | `SET FS {"Op": "UploadStart", "FileName", "FileExtension", "FileSize", "CurrentID"}` | `Op`, `CurrentID`, `ChunkSize`, `CFG.FS.Upload` |
45
+ | `SET FS {"Op": "UploadChunk", "ChunkIndex", "Data", "CurrentID"}` | `Op`, `ChunkIndex`, `CurrentID`, раз в 5% `CFG.FS.Upload` |
46
+ | `SET FS {"Op": "UploadDone", "CRC32", "CurrentID"}` | `Op`, `CurrentID`, `CFG.FS` — снимок |
47
+
48
+ Ошибки — `ER! FS` с тем же `Op` (и `CurrentID` у закачки); ошибка закачки несёт снимок `CFG.FS`.
24
49
 
25
50
  ## Пропсы
26
51
 
27
- | Название | Тип | По умолчанию | Описание |
28
- | ---------------- | ---------------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
29
- | `id` | `string` | `undefined` | Префикс прошивочной группы (обычно `"CFG"`), используется вызывающим кодом для чтения `FSInfo`/`UlProg` |
30
- | `wrapperClass` | `string` | `""` | CSS-классы обёртки |
31
- | `componentClass` | `string` | `""` | «Мастер-цвет» виджета — роль вида `"bg-red"` (см. `optionsStore.COLOR_OPTIONS`) |
32
- | `label` | `{ name?: string }` | `{ name: "Файловая система" }` | Заголовок карточки |
33
- | `value` | `IWidgetFileSystemInfo` | `undefined` | Состояние файловой системы (`CFG.FSInfo`) |
34
- | `uploadProgress` | `number` | `0` | Прогресс текущей закачки, % (`CFG.UlProg`) |
35
- | `keys` | `{ FSInfo?: string; UlProg?: string }` | `undefined` | Переопределение имён полей устройства, по умолчанию `FSInfo` и `UlProg` |
36
- | `accept` | `string` | `"*/*"` | Какие файлы можно выбрать (атрибут `accept`), например `".bin, .txt, .pem"` |
37
- | `maxNameLength` | `number` | `31` | Максимальная длина имени файла |
38
- | `infoCommand` | `{ header?: string; argument?: string }` | `undefined` | Команда обновления, по умолчанию `GET FSInfo` (используется вызывающим кодом) |
39
- | `deleteCommand` | `{ header?: string; argument?: string }` | `undefined` | Команда удаления, по умолчанию `SET DelFile` (используется вызывающим кодом) |
40
- | `collapsed` | `boolean` | `false` | Свёрнуто ли тело виджета (`$bindable`) |
41
- | `persistKey` | `string` | `undefined` | Ключ для сохранения `collapsed` в `localStorage` |
42
- | `onRefresh` | `() => void` | `undefined` | Кнопка «Обновить» в шапке; без обработчика кнопка неактивна |
43
- | `onDelete` | `(name: string) => void` | `undefined` | Удаление файла после подтверждения; `name` — полный путь из `FSList` |
44
- | `onUpload` | `(file: File) => void \| Promise<void>` | `undefined` | Закачка выбранного файла; пока промис не завершён — показывается прогресс, исключение показывается как ошибка под полем |
52
+ | Название | Тип | По умолчанию | Описание |
53
+ | ---------------- | --------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------ |
54
+ | `id` | `string` | `undefined` | Префикс прошивочной группы (обычно `"CFG"`), используется вызывающим кодом для чтения `FS` |
55
+ | `wrapperClass` | `string` | `""` | CSS-классы обёртки |
56
+ | `componentClass` | `string` | `""` | «Мастер-цвет» виджета — роль вида `"bg-red"`, перекрашивает весь виджет |
57
+ | `label` | `{ name?: string }` | `{ name: "Файловая система" }` | Заголовок карточки |
58
+ | `value` | `IWidgetFileSystemInfo` | `undefined` | Состояние файловой системы (`CFG.FS`) |
59
+ | `keys` | `{ FS?: string }` | `undefined` | Переопределение имени поля устройства, по умолчанию `FS` |
60
+ | `argument` | `string` | `undefined` | Аргумент команд, по умолчанию `"FS"` (используется вызывающим кодом) |
61
+ | `accept` | `string` | `"*/*"` | Какие файлы можно выбрать (атрибут `accept`), например `".bin, .txt, .pem"` |
62
+ | `maxNameLength` | `number` | `31` | Максимальная длина имени файла |
63
+ | `collapsed` | `boolean` | `false` | Свёрнуто ли тело виджета (`$bindable`) |
64
+ | `persistKey` | `string` | `undefined` | Ключ для сохранения `collapsed` в `localStorage` |
65
+ | `onRefresh` | `() => void` | `undefined` | Кнопка «Обновить» в шапке (`GET FS`); без обработчика кнопка неактивна |
66
+ | `onDelete` | `(name: string) => void` | `undefined` | Удаление файла после подтверждения; `name` — полный путь из `List` |
67
+ | `onUpload` | `(file: File) => void \| Promise<void>` | `undefined` | Закачка выбранного файла; исключение показывается как ошибка под полем |
45
68
 
46
69
  ### IWidgetFileSystemInfo
47
70
 
48
- | Поле | Тип | Описание |
49
- | ----------- | ---------------------------------- | ----------------------------------------------------------------- |
50
- | `FSTotal` | `number` | Размер файловой системы, байт |
51
- | `FSUsed` | `number` | Занято, байт |
52
- | `FSFree` | `number` | Свободно, байт |
53
- | `FileCount` | `number` | Количество файлов (если нет — длина `FSList`) |
54
- | `FSList` | `{ Name: string; Size: number }[]` | Файлы: полный путь (его же принимает `DelFile`) и размер в байтах |
71
+ | Поле | Тип | Описание |
72
+ | -------- | --------------------------------------------------- | ------------------------------------------------------- |
73
+ | `Total` | `number` | Размер файловой системы, байт |
74
+ | `Used` | `number` | Занято, байт |
75
+ | `Free` | `number` | Свободно, байт |
76
+ | `List` | `{ Name: string; Size: number }[]` | Файлы: полный путь (его же принимает `Delete`) и размер |
77
+ | `Upload` | `{ Name: string; Size: number; Progress: number }?` | Текущая закачка, `Progress` 0–100 с шагом 5% |
55
78
 
56
79
  ## Примеры
57
80
 
@@ -59,17 +82,15 @@
59
82
  <script>
60
83
  import * as UI from "poe-svelte-ui-lib"
61
84
 
62
- let fsInfo = { FSTotal: 1441792, FSUsed: 98304, FSFree: 1343488, FileCount: 1, FSList: [{ Name: "/littlefs/cert.pem", Size: 1834 }] }
63
- let progress = 0
85
+ let fs = { Total: 1441792, Used: 98304, Free: 1343488, List: [{ Name: "/storage/cert.pem", Size: 1834 }] }
64
86
  </script>
65
87
 
66
88
  <UI.WidgetFileSystem
67
- value={fsInfo}
68
- uploadProgress={progress}
89
+ value={fs}
69
90
  accept=".bin, .txt, .pem"
70
- onRefresh={() => console.log("GET FSInfo")}
71
- onDelete={(name) => console.log("SET DelFile", { Name: name })}
72
- onUpload={async (file) => console.log("FUpS/FUpP/FUpD", file.name)}
91
+ onRefresh={() => console.log("GET FS")}
92
+ onDelete={(name) => console.log("SET FS", { Op: "Delete", Name: name })}
93
+ onUpload={async (file) => console.log("SET FS UploadStart/UploadChunk/UploadDone", file.name)}
73
94
  />
74
95
  ```
75
96
 
@@ -81,4 +102,4 @@
81
102
  | `onPropertyChange` | `(updates: Partial<{ properties?: string \| object; name?: string; access?: string }>) => void` | Коллбэк для обновления свойств компонента |
82
103
 
83
104
  Группы панели: общие (заголовок, префикс группы, мастер-цвет), закачка (`accept`,
84
- `maxNameLength`), ключи устройства (`FSInfo`, `UlProg`), команды обновления и удаления.
105
+ `maxNameLength`), устройство (поле `FS`, аргумент команд).
@@ -1,8 +1,8 @@
1
1
  <!-- $lib/WidgetFileSystem/WidgetFileSystem.svelte — смарт-виджет файловой системы устройства.
2
- Данные — объект CFG.FSInfo в том виде, в каком его отдаёт прошивка (FS_WriteFullInfo, ProdFactory-ESP):
3
- FSTotal/FSUsed/FSFree/FileCount/FSList[{Name, Size}]; прогресс закачки — CFG.UlProg.
4
- Ничего не знает про DeviceStore/WebSocket — value/uploadProgress/onRefresh/onDelete/onUpload,
5
- протокол закачки (FUpS/FUpP/FUpD) и отправка команд на стороне вызывающего. -->
2
+ Данные — один объект CFG.FS в том виде, в каком его отдаёт прошивка (FS_WriteState, ProdFactory-ESP):
3
+ Total/Used/Free/List[{Name, Size}] и Upload{Name, Size, Progress} — только пока идёт закачка.
4
+ Ничего не знает про DeviceStore/WebSocket — value/onRefresh/onDelete/onUpload, протокол
5
+ закачки (SET FS с Op UploadStart/UploadChunk/UploadDone) и отправка команд на стороне вызывающего. -->
6
6
  <script lang="ts">
7
7
  import { slide, fade, scale } from "svelte/transition"
8
8
  import { twMerge } from "tailwind-merge"
@@ -19,7 +19,6 @@
19
19
  componentClass = "",
20
20
  label = { name: "Файловая система" },
21
21
  value,
22
- uploadProgress = 0,
23
22
  accept = "*/*",
24
23
  maxNameLength = 31,
25
24
  collapsed = $bindable(false),
@@ -49,10 +48,10 @@
49
48
  return `${(bytes / (1024 * 1024)).toFixed(1)} МБ`
50
49
  }
51
50
 
52
- let total = $derived(value?.FSTotal ?? 0)
53
- let used = $derived(value?.FSUsed ?? 0)
54
- let free = $derived(value?.FSFree ?? Math.max(total - used, 0))
55
- let files = $derived(value?.FSList ?? [])
51
+ let total = $derived(value?.Total ?? 0)
52
+ let used = $derived(value?.Used ?? 0)
53
+ let free = $derived(value?.Free ?? Math.max(total - used, 0))
54
+ let files = $derived(value?.List ?? [])
56
55
  let usedPercent = $derived(total > 0 ? Math.min(100, Math.round((used / total) * 100)) : 0)
57
56
  /* Полоса — мастер-цвет виджета; почти заполненную ФС (закачка скоро начнёт падать) выдаёт только процент */
58
57
  let percentColor = $derived(usedPercent >= 90 ? "var(--red-color)" : usedPercent >= 75 ? "var(--orange-color)" : "var(--muted-color)")
@@ -75,8 +74,10 @@
75
74
  : []),
76
75
  ])
77
76
 
78
- /* Прогресс закачки 0..100; нечисло (NaN, массив в формате ProgressBar и т.п.) — 0, а не «NaN%» */
79
- let progress = $derived(Number.isFinite(Number(uploadProgress)) ? Math.min(100, Math.max(0, Number(uploadProgress))) : 0)
77
+ /* Закачка видна, пока её ведёт этот клиент или пока устройство сообщает Upload (закачка с другого клиента);
78
+ прогресс 0..100, нечисло — 0, а не «NaN%» */
79
+ let upload = $derived(value?.Upload)
80
+ let progress = $derived(Number.isFinite(Number(upload?.Progress)) ? Math.min(100, Math.max(0, Number(upload?.Progress))) : 0)
80
81
 
81
82
  let pendingDelete: string | null = $state(null)
82
83
  const confirmDelete = () => {
@@ -84,8 +85,7 @@
84
85
  pendingDelete = null
85
86
  }
86
87
 
87
- /* Закачка: имя проверяется до отправки (прошивка отвергнет длинное имя уже после FUpS), прогресс —
88
- из uploadProgress, пока onUpload не завершится */
88
+ /* Закачка: имя проверяется до отправки (прошивка отвергнет длинное имя уже после UploadStart) */
89
89
  let uploading = $state(false)
90
90
  let uploadError = $state("")
91
91
  const handleFile = async (_event: Event, file: File | null) => {
@@ -136,7 +136,7 @@
136
136
  </div>
137
137
  <div class="flex justify-between gap-2 text-[12px] text-(--muted-color)">
138
138
  <span>Свободно {formatBytes(free)}</span>
139
- <span>Файлов: {value?.FileCount ?? files.length}</span>
139
+ <span>Файлов: {files.length}</span>
140
140
  </div>
141
141
  </div>
142
142
 
@@ -152,9 +152,12 @@
152
152
  <!-- Закачка -->
153
153
  {#if onUpload}
154
154
  <div class="flex flex-col gap-2 rounded-[10px] border border-(--hairline-color) bg-(--back-color) p-3">
155
- <UI.FileAttach label={{ name: "Закачать файл" }} {accept} disabled={uploading} onChange={handleFile} />
156
- {#if uploading}
155
+ <UI.FileAttach label={{ name: "Закачать файл" }} {accept} disabled={uploading || !!upload} onChange={handleFile} />
156
+ {#if uploading || upload}
157
157
  <div class="flex items-center gap-2 text-[12px]" transition:slide={{ duration: 100 }}>
158
+ {#if upload?.Name}
159
+ <span class="max-w-40 truncate text-(--muted-color)">{upload.Name}</span>
160
+ {/if}
158
161
  <div class="h-2 flex-1 overflow-hidden rounded-full bg-(--container-color)">
159
162
  <div class="h-full rounded-full bg-(--accent-color) transition-[width] duration-300" style="width: {progress}%"></div>
160
163
  </div>
@@ -1,16 +1,13 @@
1
1
  <!-- $lib/WidgetFileSystem/WidgetFileSystemProps.svelte — панель пропсов конструктора для
2
- WidgetFileSystem: заголовок/мастер-цвет/префикс группы, имена полей устройства (keys),
3
- фильтр закачки и команды обновления/удаления — все с дефолтами, равными прошивочной
4
- конвенции (см. DEFAULT_PROPS.WidgetFileSystem в DevCloud). -->
2
+ WidgetFileSystem: заголовок/мастер-цвет/префикс группы, имя поля устройства (keys.FS),
3
+ фильтр закачки и аргумент команд — все с дефолтами, равными прошивочной конвенции
4
+ (см. DEFAULT_PROPS.WidgetFileSystem в DevCloud). -->
5
5
  <script lang="ts">
6
6
  import { T } from "../locales/i18n"
7
7
  import * as UI from ".."
8
- import { updateProperty, type UIComponent, type IWidgetFileSystemProps, type IOption } from "../types"
8
+ import { updateProperty, type UIComponent, type IWidgetFileSystemProps } from "../types"
9
9
  import WidgetAccentPicker from "../WidgetAccentPicker.svelte"
10
10
  import PropsGroup from "../PropsGroup.svelte"
11
- import { optionsStore } from "../options"
12
-
13
- const FS_KEYS = ["FSInfo", "UlProg"] as const
14
11
 
15
12
  const { component, onPropertyChange } = $props<{
16
13
  component: UIComponent & { properties: Partial<IWidgetFileSystemProps> }
@@ -18,21 +15,6 @@
18
15
  }>()
19
16
  </script>
20
17
 
21
- {#snippet Command(key: "infoCommand" | "deleteCommand", header: string, argument: string)}
22
- <UI.Select
23
- label={{ name: "Заголовок пакета" }}
24
- type="buttons"
25
- options={$optionsStore.HEADER_OPTIONS}
26
- value={$optionsStore.HEADER_OPTIONS.find((o) => o.value === (component.properties[key]?.header || header))}
27
- onUpdate={(option) => updateProperty(`${key}.header`, (option as IOption<string>).value as string, component, onPropertyChange)}
28
- />
29
- <UI.Input
30
- label={{ name: "Аргумент" }}
31
- value={component.properties[key]?.argument ?? argument}
32
- onUpdate={(value) => updateProperty(`${key}.argument`, value as string, component, onPropertyChange)}
33
- />
34
- {/snippet}
35
-
36
18
  <div class="flex flex-col gap-2">
37
19
  <PropsGroup label={$T("constructor.props.group.general")}>
38
20
  <UI.Input
@@ -65,19 +47,16 @@
65
47
  onUpdate={(value) => updateProperty("maxNameLength", Number(value), component, onPropertyChange)}
66
48
  />
67
49
  </PropsGroup>
68
- <PropsGroup label="Ключи устройства">
69
- {#each FS_KEYS as key (key)}
70
- <UI.Input
71
- label={{ name: key }}
72
- value={component.properties.keys?.[key] ?? key}
73
- onUpdate={(value) => updateProperty(`keys.${key}`, value as string, component, onPropertyChange)}
74
- />
75
- {/each}
76
- </PropsGroup>
77
- <PropsGroup label="Команда обновления">
78
- {@render Command("infoCommand", "GET", "FSInfo")}
79
- </PropsGroup>
80
- <PropsGroup label="Команда удаления">
81
- {@render Command("deleteCommand", "SET", "DelFile")}
50
+ <PropsGroup label="Устройство">
51
+ <UI.Input
52
+ label={{ name: "Поле состояния ФС" }}
53
+ value={component.properties.keys?.FS ?? "FS"}
54
+ onUpdate={(value) => updateProperty("keys.FS", value as string, component, onPropertyChange)}
55
+ />
56
+ <UI.Input
57
+ label={{ name: "Аргумент команд" }}
58
+ value={component.properties.argument ?? "FS"}
59
+ onUpdate={(value) => updateProperty("argument", value as string, component, onPropertyChange)}
60
+ />
82
61
  </PropsGroup>
83
62
  </div>
package/dist/index.d.ts CHANGED
@@ -48,4 +48,4 @@ export { default as WidgetFileSystemProps } from "./WidgetFileSystem/WidgetFileS
48
48
  export * from "./locales/i18n";
49
49
  export * from "./locales/translations";
50
50
  export * from "./ModalStackStore";
51
- export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type IOption, type ISwitchProps, type IColorPickerProps, type IDividerProps, type ISliderProps, type ITextFieldProps, type IMapProps, type IProgressBarProps, type IWidgetProps, type IWidgetWiFiProps, type IWidgetWiFiConfig, type IWidgetDeviceInfoProps, type IWidgetDeviceInfoConfig, type IWidgetFileSystemProps, type IWidgetFileSystemInfo, type IWidgetFileSystemFile, type IGraphProps, type IGraphDataObject, type IGraphHistoryPoint, type IGraphHistorySeries, type ITableHeader, type ITableText, type ITableSelect, type ITableButton, type ITableProgressBar, type ITableImage, type ITableContent, type ITableProps, type ITabsProps, type IJoystickProps, type IFileAttachProps, type IDeviceGNSS, type ICarouselProps, type IReceivingDataObject, type IVideoViewerProps, type IPeriodChartProps, type IPeriodChartLevel, } from "./types";
51
+ export { type UIComponent, type Position, type IUIComponentHandler, type IButtonProps, type IAccordionProps, type IInputProps, type ISelectProps, type IOption, type ISwitchProps, type IColorPickerProps, type IDividerProps, type ISliderProps, type ITextFieldProps, type IMapProps, type IProgressBarProps, type IWidgetProps, type IWidgetWiFiProps, type IWidgetWiFiConfig, type IWidgetDeviceInfoProps, type IWidgetDeviceInfoConfig, type IWidgetFileSystemProps, type IWidgetFileSystemInfo, type IWidgetFileSystemFile, type IWidgetFileSystemUpload, type IGraphProps, type IGraphDataObject, type IGraphHistoryPoint, type IGraphHistorySeries, type ITableHeader, type ITableText, type ITableSelect, type ITableButton, type ITableProgressBar, type ITableImage, type ITableContent, type ITableProps, type ITabsProps, type IJoystickProps, type IFileAttachProps, type IDeviceGNSS, type ICarouselProps, type IReceivingDataObject, type IVideoViewerProps, type IPeriodChartProps, type IPeriodChartLevel, } from "./types";
@@ -24,9 +24,9 @@ const translations = {
24
24
  "ℹ️ 该组件使用设备 `CFG` 分组下的一组固定字段——无需手动绑定变量。\n\n**🔒 只读字段**\n- `DevSN`、`DevID`、`DevFW`、`RunCnt`\n- `StaIP`、`ApIP` —— STA/AP 模式下的当前 IP 地址\n\n**✏️ 可编辑字段**(通过「保存」按钮一次性发送)\n- `DevName`、`HostName`、`WebUser`、`WebPsw`\n\n🔌 「重启」按钮会发送 `Argument: Restart`。\n\n⚙️ 如果设备使用的分组名称不是 `CFG`,请在组件设置中修改前缀(ID)。",
25
25
  ],
26
26
  "component.info.WidgetFileSystem": [
27
- "🗂️ Виджет работает с объектом `FSInfo` группы `CFG` на устройстве — привязка переменных вручную не нужна.\n\n**📊 Занятость**\n- `FSTotal`, `FSUsed`, `FSFree`, `FileCount`\n\n**📄 Файлы**\n- `FSList` — путь и размер; «Удалить» отправляет `DelFile` с `Name` (после подтверждения)\n\n**⬆️ Закачка**\n- протокол `FUpS` → `FUpP` → `FUpD`, прогресс — `UlProg`\n- имя файла не длиннее 31 символа (ограничение прошивки)\n\n🔄 «Обновить» отправляет `GET FSInfo`.\n\n⚙️ Если у прошивки другие имена, поменяйте префикс (ID) и ключи в настройках виджета.",
28
- "🗂️ The widget works with the `FSInfo` object of the device's `CFG` group — no manual variable binding needed.\n\n**📊 Usage**\n- `FSTotal`, `FSUsed`, `FSFree`, `FileCount`\n\n**📄 Files**\n- `FSList` — path and size; \"Delete\" sends `DelFile` with `Name` (after confirmation)\n\n**⬆️ Upload**\n- `FUpS` → `FUpP` → `FUpD` protocol, progress — `UlProg`\n- file name up to 31 characters (firmware limit)\n\n🔄 \"Refresh\" sends `GET FSInfo`.\n\n⚙️ If the firmware uses different names, change the prefix (ID) and keys in the widget settings.",
29
- "🗂️ 该组件使用设备 `CFG` 分组下的 `FSInfo` 对象——无需手动绑定变量。\n\n**📊 占用**\n- `FSTotal`、`FSUsed`、`FSFree`、`FileCount`\n\n**📄 文件**\n- `FSList` —— 路径和大小;「删除」在确认后发送带 `Name` 的 `DelFile`\n\n**⬆️ 上传**\n- `FUpS` → `FUpP` → `FUpD` 协议,进度 —— `UlProg`\n- 文件名不超过 31 个字符(固件限制)\n\n🔄 「刷新」发送 `GET FSInfo`。\n\n⚙️ 如果固件使用其他名称,请在组件设置中修改前缀(ID)和键名。",
27
+ "🗂️ Виджет работает с одним объектом `FS` группы `CFG` на устройстве — привязка переменных вручную не нужна.\n\n**📊 Занятость**\n- `Total`, `Used`, `Free`\n\n**📄 Файлы**\n- `List` — путь и размер; «Удалить» отправляет `SET FS` с `Op: Delete` и `Name` (после подтверждения)\n\n**⬆️ Закачка**\n- `SET FS` с `Op`: `UploadStart` → `UploadChunk` → `UploadDone`, прогресс — `Upload.Progress`\n- имя файла не длиннее 31 символа (ограничение прошивки)\n\n🔄 «Обновить» отправляет `GET FS`.\n\n⚙️ Если у прошивки другие имена, поменяйте префикс (ID), поле и аргумент в настройках виджета.",
28
+ "🗂️ The widget works with a single `FS` object of the device's `CFG` group — no manual variable binding needed.\n\n**📊 Usage**\n- `Total`, `Used`, `Free`\n\n**📄 Files**\n- `List` — path and size; \"Delete\" sends `SET FS` with `Op: Delete` and `Name` (after confirmation)\n\n**⬆️ Upload**\n- `SET FS` with `Op`: `UploadStart` → `UploadChunk` → `UploadDone`, progress — `Upload.Progress`\n- file name up to 31 characters (firmware limit)\n\n🔄 \"Refresh\" sends `GET FS`.\n\n⚙️ If the firmware uses different names, change the prefix (ID), field and argument in the widget settings.",
29
+ "🗂️ 该组件使用设备 `CFG` 分组下的单个 `FS` 对象——无需手动绑定变量。\n\n**📊 占用**\n- `Total`、`Used`、`Free`\n\n**📄 文件**\n- `List` —— 路径和大小;「删除」在确认后发送带 `Op: Delete` 和 `Name` 的 `SET FS`\n\n**⬆️ 上传**\n- `SET FS`,`Op`:`UploadStart` → `UploadChunk` → `UploadDone`,进度 —— `Upload.Progress`\n- 文件名不超过 31 个字符(固件限制)\n\n🔄 「刷新」发送 `GET FS`。\n\n⚙️ 如果固件使用其他名称,请在组件设置中修改前缀(ID)、字段和参数。",
30
30
  ],
31
31
  "component.info.Tabs": [
32
32
  "📑 Ширина и позиция иконки задаются через служебные CSS-классы каждой вкладки, а не отдельными полями.\n\n**↔️ Ширина кнопок** (переключатель «Авто/Равные»)\n- `w-auto` — по содержимому\n- `flex-1` — поровну между всеми вкладками\n\n**🔤 Позиция иконки**\n- `flex-row` / `flex-col` / `flex-row-reverse` / `flex-col-reverse`\n\n⚠️ Максимум 10 вкладок.",
package/dist/types.d.ts CHANGED
@@ -564,12 +564,17 @@ export interface IWidgetFileSystemFile {
564
564
  Name: string;
565
565
  Size: number;
566
566
  }
567
+ export interface IWidgetFileSystemUpload {
568
+ Name: string;
569
+ Size: number;
570
+ Progress: number;
571
+ }
567
572
  export interface IWidgetFileSystemInfo {
568
- FSTotal: number;
569
- FSUsed: number;
570
- FSFree: number;
571
- FileCount?: number;
572
- FSList: IWidgetFileSystemFile[];
573
+ Total: number;
574
+ Used: number;
575
+ Free: number;
576
+ List: IWidgetFileSystemFile[];
577
+ Upload?: IWidgetFileSystemUpload;
573
578
  }
574
579
  export interface IWidgetFileSystemProps {
575
580
  id?: string;
@@ -579,21 +584,12 @@ export interface IWidgetFileSystemProps {
579
584
  name?: string;
580
585
  };
581
586
  value?: IWidgetFileSystemInfo;
582
- uploadProgress?: number;
583
587
  keys?: {
584
- FSInfo?: string;
585
- UlProg?: string;
588
+ FS?: string;
586
589
  };
590
+ argument?: string;
587
591
  accept?: string;
588
592
  maxNameLength?: number;
589
- infoCommand?: {
590
- header?: string;
591
- argument?: string;
592
- };
593
- deleteCommand?: {
594
- header?: string;
595
- argument?: string;
596
- };
597
593
  collapsed?: boolean;
598
594
  persistKey?: string;
599
595
  onRefresh?: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-svelte-ui-lib",
3
- "version": "1.32.2",
3
+ "version": "1.33.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {