poe-svelte-ui-lib 1.88.0 → 1.89.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.
Files changed (103) hide show
  1. package/README.md +1 -1
  2. package/dist/Accordion/Accordion.svelte +2 -2
  3. package/dist/Accordion/README.md +1 -1
  4. package/dist/Button/Button.svelte +2 -2
  5. package/dist/Button/ButtonProps.svelte +1 -1
  6. package/dist/Button/README.md +1 -1
  7. package/dist/CommonSnippets.svelte +3 -2
  8. package/dist/CopiedChip.svelte +1 -1
  9. package/dist/Divider/Divider.svelte +1 -1
  10. package/dist/Divider/README.md +1 -1
  11. package/dist/Dragging.svelte +4 -4
  12. package/dist/FileAttach/FileAttach.svelte +4 -4
  13. package/dist/Graph/Graph.svelte +10 -10
  14. package/dist/Graph/README.md +1 -1
  15. package/dist/Input/Input.svelte +2 -2
  16. package/dist/Input/InputProps.svelte +7 -3
  17. package/dist/Joystick/Joystick.svelte +1 -1
  18. package/dist/Modal.svelte +2 -2
  19. package/dist/PeriodChart/PeriodChart.svelte +3 -3
  20. package/dist/PeriodChart/PeriodChartProps.svelte +1 -1
  21. package/dist/ProgressBar/ProgressBar.svelte +1 -1
  22. package/dist/PropsGroup.svelte +1 -1
  23. package/dist/PropsListHeader.svelte +1 -1
  24. package/dist/PropsListModal.svelte +1 -1
  25. package/dist/Select/Select.svelte +4 -4
  26. package/dist/Select/SelectProps.svelte +2 -2
  27. package/dist/Slider/Slider.svelte +8 -8
  28. package/dist/Switch/Switch.svelte +3 -3
  29. package/dist/Switch/SwitchProps.svelte +1 -1
  30. package/dist/Table/Table.svelte +4 -4
  31. package/dist/Table/TableProps.svelte +3 -3
  32. package/dist/Table/columnWidth.js +1 -1
  33. package/dist/Tabs/Tabs.svelte +3 -3
  34. package/dist/Tabs/TabsProps.svelte +2 -2
  35. package/dist/TextField/TextField.svelte +2 -2
  36. package/dist/TextsEditor.svelte +3 -3
  37. package/dist/Tooltip.svelte +1 -1
  38. package/dist/Widget/Widget.svelte +3 -3
  39. package/dist/Widget/WidgetProps.svelte +1 -1
  40. package/dist/WidgetAccentPicker.svelte +1 -1
  41. package/dist/WidgetDeviceInfo/README.md +1 -1
  42. package/dist/WidgetDeviceInfo/WidgetDeviceInfo.svelte +9 -9
  43. package/dist/WidgetDeviceInfo/WidgetDeviceInfoProps.svelte +1 -1
  44. package/dist/WidgetFileSystem/README.md +1 -1
  45. package/dist/WidgetFileSystem/WidgetFileSystem.svelte +7 -7
  46. package/dist/WidgetFileSystem/WidgetFileSystemProps.svelte +1 -1
  47. package/dist/WidgetHeader.svelte +4 -4
  48. package/dist/WidgetMap/README.md +58 -58
  49. package/dist/WidgetMap/WidgetMap.svelte +13 -13
  50. package/dist/WidgetMap/WidgetMapProps.svelte +1 -1
  51. package/dist/WidgetMap/mapWrapper/Line.svelte +2 -2
  52. package/dist/WidgetMap/mapWrapper/MapLibre.svelte +3 -3
  53. package/dist/WidgetMap/mapWrapper/utils.js +2 -2
  54. package/dist/WidgetMap/mission/MapAgro.svelte +20 -27
  55. package/dist/WidgetMap/mission/MapRoute.svelte +3 -6
  56. package/dist/WidgetMap/mission/MissionLayers.svelte +3 -3
  57. package/dist/WidgetMap/mission/MissionSim.svelte +3 -3
  58. package/dist/WidgetMap/mission/SpinnerIcon.svelte +1 -1
  59. package/dist/WidgetMap/mission/coverage.js +7 -7
  60. package/dist/WidgetMap/mission/progress.js +1 -1
  61. package/dist/WidgetMap/mission/route.js +1 -1
  62. package/dist/WidgetPFD/README.md +9 -9
  63. package/dist/WidgetPFD/WidgetPFD.svelte +26 -26
  64. package/dist/WidgetPFD/WidgetPFDProps.svelte +2 -2
  65. package/dist/WidgetRGBStrip/ColorPicker.svelte +1 -1
  66. package/dist/WidgetRGBStrip/README.md +1 -1
  67. package/dist/WidgetRGBStrip/WidgetRGBStrip.svelte +7 -7
  68. package/dist/WidgetRGBStrip/WidgetRGBStripProps.svelte +1 -1
  69. package/dist/WidgetStackInfo/README.md +1 -1
  70. package/dist/WidgetStackInfo/WidgetStackInfo.svelte +10 -10
  71. package/dist/WidgetStackInfo/WidgetStackInfoProps.svelte +1 -1
  72. package/dist/WidgetWiFi/README.md +1 -1
  73. package/dist/WidgetWiFi/WidgetWiFi.svelte +11 -11
  74. package/dist/WidgetWiFi/WidgetWiFiProps.svelte +1 -1
  75. package/dist/bitField.js +1 -1
  76. package/dist/dragStyle.js +3 -3
  77. package/dist/hints.js +4 -4
  78. package/dist/libIcons/propsIcons.js +1 -1
  79. package/dist/locales/i18n.js +9 -9
  80. package/dist/locales/index.js +1 -1
  81. package/dist/locales/languages.js +4 -4
  82. package/dist/locales/localize.js +7 -7
  83. package/dist/locales/translations.js +243 -283
  84. package/dist/options.js +2 -2
  85. package/dist/protocols/can.js +6 -6
  86. package/dist/protocols/cbor/decoder.js +2 -2
  87. package/dist/protocols/cbor/encoder.js +1 -1
  88. package/dist/protocols/cbor/json.js +2 -2
  89. package/dist/protocols/cbor/value.js +4 -4
  90. package/dist/protocols/crc.js +2 -2
  91. package/dist/protocols/dictionary.js +2 -2
  92. package/dist/protocols/dictionary.yaml +5 -5
  93. package/dist/protocols/dictionaryCodes.js +1 -1
  94. package/dist/protocols/index.js +6 -6
  95. package/dist/protocols/packet.js +11 -11
  96. package/dist/protocols/plotGroups.js +2 -2
  97. package/dist/protocols/xPoeDebug.js +8 -8
  98. package/dist/protocols/xPoeDebugView.js +8 -8
  99. package/dist/theme.css +12 -12
  100. package/dist/types.js +1 -1
  101. package/dist/widgetAccent.js +4 -4
  102. package/dist/widgetCollapse.js +2 -2
  103. package/package.json +1 -1
package/README.md CHANGED
@@ -74,7 +74,7 @@ npm install poe-svelte-ui-lib
74
74
  |---|---|
75
75
  | `component` | Компонент интерфейса и его свойства |
76
76
  | `onPropertyChange` | Сообщает об изменении свойств, имени, доступа или события |
77
- | `forConstructor` | `true` — полная панель конструктора, `false` — упрощённая для редактирования |
77
+ | `forConstructor` | `true` - полная панель конструктора, `false` - упрощённая для редактирования |
78
78
 
79
79
  ## Протоколы
80
80
 
@@ -1,4 +1,4 @@
1
- <!-- $lib/Accordion/Accordion.svelte — заголовок не увеличивается при раскрытии, радиус 14. Мастер-цвет (componentClass) — как у смарт-виджетов: иконка и текст заголовка цветом, фон заголовка — его мягким оттенком. -->
1
+ <!-- $lib/Accordion/Accordion.svelte - заголовок не увеличивается при раскрытии, радиус 14. Мастер-цвет (componentClass) - как у смарт-виджетов: иконка и текст заголовка цветом, фон заголовка - его мягким оттенком. -->
2
2
  <script lang="ts">
3
3
  import { slide } from "svelte/transition"
4
4
  import type { IAccordionProps } from "../types"
@@ -26,7 +26,7 @@
26
26
  transition:slide={{ duration: 200 }}
27
27
  >
28
28
  <div class="overflow-hidden rounded-[14px] border border-(--hairline-color) bg-(--back-color)">
29
- <!-- Фон заголовка — мягкий оттенок мастер-цвета (светлый в светлой теме, тёмный в тёмной), текст — мастер-цвет с цветом шрифта 60/40, раскрытое содержимое остаётся на чистом фоне (--back-color). -->
29
+ <!-- Фон заголовка - мягкий оттенок мастер-цвета (светлый в светлой теме, тёмный в тёмной), текст - мастер-цвет с цветом шрифта 60/40, раскрытое содержимое остаётся на чистом фоне (--back-color). -->
30
30
  <button
31
31
  class="flex w-full cursor-pointer items-center justify-between gap-3 bg-(--accent-soft) px-4 py-3 text-left text-[color-mix(in_srgb,var(--accent-color)_60%,var(--font-color))] transition-colors duration-150 hover:bg-[color-mix(in_srgb,var(--accent-color)_12%,var(--accent-soft))]"
32
32
  onclick={toggle}
@@ -26,7 +26,7 @@
26
26
  | `id` | `string` | `crypto.randomUUID()` | Идентификатор |
27
27
  | `isOpen` | `boolean` | `false` | Открыт ли. Можно связать через `bind:isOpen` |
28
28
  | `wrapperClass` | `string` | `""` | Классы обёртки |
29
- | `componentClass` | `string` | `""` | Мастер-цвет: `bg-red`, `bg-green` и другие из `COLOR_OPTIONS`. Пусто — акцент темы |
29
+ | `componentClass` | `string` | `""` | Мастер-цвет: `bg-red`, `bg-green` и другие из `COLOR_OPTIONS`. Пусто - акцент темы |
30
30
  | `size` | `{ width, height }` | `1 × 1` | Сетка содержимого: колонки и строки |
31
31
  | `label.name` | `string` | `""` | Заголовок |
32
32
  | `label.class` | `string` | `"text-left"` | Классы текста заголовка |
@@ -1,4 +1,4 @@
1
- <!-- $lib/Button/Button.svelte — кнопка: роль по значению bg-*, радиус 10px (иконка без подписи — круг), высота 32px, кольцо фокуса, иконка в потоке через flex + gap -->
1
+ <!-- $lib/Button/Button.svelte - кнопка: роль по значению bg-*, радиус 10px (иконка без подписи - круг), высота 32px, кольцо фокуса, иконка в потоке через flex + gap -->
2
2
  <script lang="ts">
3
3
  import { onMount } from "svelte"
4
4
  import type { IButtonProps } from "../types"
@@ -39,7 +39,7 @@
39
39
 
40
40
  const isIconOnly = $derived(!!content?.icon && !content.name?.trim())
41
41
 
42
- /* bg-(--bg-color) намеренно не входит в roleClass: он приклеивается к готовому className вне twMerge (см. разметку ниже) — иначе twMerge видит его и явный bg-* из componentClass (bg-red, bg-max…) как конфликтующие утилиты одной группы и вырезает один из них, из-за чего заливка кнопки пропадает. */
42
+ /* bg-(--bg-color) намеренно не входит в roleClass: он приклеивается к готовому className вне twMerge (см. разметку ниже) - иначе twMerge видит его и явный bg-* из componentClass (bg-red, bg-max…) как конфликтующие утилиты одной группы и вырезает один из них, из-за чего заливка кнопки пропадает. */
43
43
  const roleBg = $derived.by(() => {
44
44
  if (disabled) return "bg-(--gray-color)"
45
45
  if (role === "plain") return "bg-transparent"
@@ -23,7 +23,7 @@
23
23
  forConstructor?: boolean
24
24
  } = $props()
25
25
 
26
- /* Argument пуст — поле ниже лишь визуально показывает "ModCfg", реальное значение нужно проставить в состояние */
26
+ /* Argument пуст - поле ниже лишь визуально показывает "ModCfg", реальное значение нужно проставить в состояние */
27
27
  $effect(() => {
28
28
  if (!component.eventHandler?.Argument) onPropertyChange({ eventHandler: { Argument: "ModCfg" } })
29
29
  })
@@ -65,4 +65,4 @@
65
65
  |---|---|---|
66
66
  | `component` | `UIComponent & { properties: Partial<IButtonProps> }` | Компонент и его свойства |
67
67
  | `onPropertyChange` | `(updates) => void` | Сообщает об изменении свойств |
68
- | `forConstructor` | `boolean` | `true` — полная панель конструктора, `false` — упрощённая для редактирования |
68
+ | `forConstructor` | `boolean` | `true` - полная панель конструктора, `false` - упрощённая для редактирования |
@@ -53,7 +53,8 @@
53
53
  onUpdate={(value) => {
54
54
  updateProperty("id", (value as UI.IOption).value as string, component, onPropertyChange)
55
55
  onPropertyChange({
56
- name: (value as UI.IOption).name?.split("—")[1]?.trim(),
56
+ /* \u041f\u043e\u0434\u043f\u0438\u0441\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430 \u00ab<id> - <\u0438\u043c\u044f>\u00bb: \u0438\u043c\u044f - \u0432\u0441\u0451 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f; \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0438 \u0434\u043b\u0438\u043d\u043d\u043e\u0435 \u0442\u0438\u0440\u0435 (\u2014) \u0441\u0442\u0430\u0440\u044b\u0445 \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 */
57
+ name: (value as UI.IOption).name?.match(/\s[\u2014-]\s(.*)/)?.[1]?.trim(),
57
58
  eventHandler: { Variables: [(value as UI.IOption).value as string] },
58
59
  })
59
60
  }}
@@ -238,7 +239,7 @@
238
239
  icons: { array: [string, string[]][]; selectArray?: false }
239
240
  icon: string | string[]
240
241
  updateProperty: (icon: string | string[]) => {}
241
- /* compact — для строк списков в одну линию: квадратная кнопка с текущей иконкой и подсказкой вместо кнопки с текстом */
242
+ /* compact - для строк списков в одну линию: квадратная кнопка с текущей иконкой и подсказкой вместо кнопки с текстом */
242
243
  compact?: boolean
243
244
  })}
244
245
  <div class={initialValue.compact ? "relative flex shrink-0 items-center gap-1" : "relative mt-6 flex w-full gap-2"}>
@@ -1,4 +1,4 @@
1
- <!-- $lib/CopiedChip.svelte — общая всплывающая метка «Скопировано» после клика на кнопку копирования -->
1
+ <!-- $lib/CopiedChip.svelte - общая всплывающая метка «Скопировано» после клика на кнопку копирования -->
2
2
  <script lang="ts">
3
3
  import { T } from "./locales/i18n"
4
4
  import { fade } from "svelte/transition"
@@ -1,4 +1,4 @@
1
- <!-- $lib/Divider/Divider.svelte — волосяная линия вместо скруглённой полосы 2px. -->
1
+ <!-- $lib/Divider/Divider.svelte - волосяная линия вместо скруглённой полосы 2px. -->
2
2
  <script lang="ts">
3
3
  import { twMerge } from "tailwind-merge"
4
4
  import type { IDividerProps } from "../types"
@@ -25,7 +25,7 @@
25
25
  | `wrapperClass` | `string` | `""` | Классы обёртки |
26
26
  | `type` | `"line" \| "empty"` | `"line"` | Линия или пустой отступ |
27
27
  | `width` | `number` | `1` | Толщина линии, пикселей |
28
- | `color` | `string` | `""` | Цвет: `bg-max`, `bg-red`, `bg-yellow`, `bg-green`, `bg-blue`, `bg-purple`, `bg-gray`. Пусто — нейтральный |
28
+ | `color` | `string` | `""` | Цвет: `bg-max`, `bg-red`, `bg-yellow`, `bg-green`, `bg-blue`, `bg-purple`, `bg-gray`. Пусто - нейтральный |
29
29
  | `anchor` | `"top" \| "center" \| "bottom"` | `"center"` | Где стоит линия по высоте |
30
30
 
31
31
  Колбэков нет.
@@ -39,7 +39,7 @@
39
39
 
40
40
  /* Перетаскивание с MouseEvent */
41
41
  const startDrag = async (e: MouseEvent) => {
42
- /* Строка ищется только внутри своего списка — id вида item-N есть и у других списков на странице */
42
+ /* Строка ищется только внутри своего списка - id вида item-N есть и у других списков на странице */
43
43
  const movedElement = container.querySelector(
44
44
  containerIndex == null ? `#${elementsId}-${elementIndex}` : `#${elementsId}-${elementIndex}-${containerIndex}`,
45
45
  ) as HTMLElement
@@ -49,7 +49,7 @@
49
49
  const element = movedElement.cloneNode(true) as HTMLElement
50
50
  const rect = movedElement.getBoundingClientRect()
51
51
 
52
- /* Визуальный эффект перетаскивания — общий для библиотеки и приложений (dragStyle.ts) */
52
+ /* Визуальный эффект перетаскивания - общий для библиотеки и приложений (dragStyle.ts) */
53
53
  element.id = "drag-element"
54
54
  element.style.position = "fixed"
55
55
  element.style.zIndex = "9999"
@@ -93,7 +93,7 @@
93
93
  const ghost = dragState.ghostElement
94
94
  ghost.style.top = `${e.clientY - dragState.offsetY}px`
95
95
 
96
- /* Место вставки — перед первой строкой, середина которой ниже курсора (иначе — в конец): меняется только при пересечении середины строки, без дрожания на границах. Соседние строки сдвигаются плавно (animateReorder) */
96
+ /* Место вставки - перед первой строкой, середина которой ниже курсора (иначе - в конец): меняется только при пересечении середины строки, без дрожания на границах. Соседние строки сдвигаются плавно (animateReorder) */
97
97
  const items = commandItems.filter((item) =>
98
98
  dragState.containerIndex === null ? true : Number(item.id.split("-")[2]) === dragState.containerIndex,
99
99
  ) as HTMLElement[]
@@ -101,7 +101,7 @@
101
101
  let target = items[items.length - 1]
102
102
  for (const item of items) {
103
103
  const rect = item.getBoundingClientRect()
104
- /* Положение в раскладке, без сдвига идущей анимации — иначе движущаяся строка снова «пересекала» бы курсор */
104
+ /* Положение в раскладке, без сдвига идущей анимации - иначе движущаяся строка снова «пересекала» бы курсор */
105
105
  const transform = getComputedStyle(item).transform
106
106
  const shift = transform && transform !== "none" ? new DOMMatrixReadOnly(transform).m42 : 0
107
107
  if (e.clientY < rect.top - shift + rect.height / 2) {
@@ -4,7 +4,7 @@
4
4
  import { twMerge } from "tailwind-merge"
5
5
  import Button from "../Button/Button.svelte"
6
6
 
7
- /* Иконка «открыть файл» для type "button" — документ со стрелкой вверх, контур 1.75 как у остальных кнопок */
7
+ /* Иконка «открыть файл» для type "button" - документ со стрелкой вверх, контур 1.75 как у остальных кнопок */
8
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>`
9
9
 
10
10
  let {
@@ -49,7 +49,7 @@
49
49
  }
50
50
 
51
51
  onChange(event, file)
52
- /* Кнопка «Открыть файл» — повторный выбор того же файла тоже должен срабатывать */
52
+ /* Кнопка «Открыть файл» - повторный выбор того же файла тоже должен срабатывать */
53
53
  if (type === "button") input.value = ""
54
54
  }
55
55
 
@@ -59,14 +59,14 @@
59
59
  }
60
60
  </script>
61
61
 
62
- <!-- По design-handoff (раздел 9): зона сброса — пунктир 1 px с радиусом 12 вместо залитой кнопки, высота как у Input; «Выбрать файл» — акцентный текст, имя файла — обычным цветом, пока не выбрано — бледным. -->
62
+ <!-- По design-handoff (раздел 9): зона сброса - пунктир 1 px с радиусом 12 вместо залитой кнопки, высота как у Input; «Выбрать файл» - акцентный текст, имя файла - обычным цветом, пока не выбрано - бледным. -->
63
63
  <div class={twMerge(`flex flex-col items-center gap-1`, wrapperClass)}>
64
64
  {#if label.name}
65
65
  <span class={twMerge(`w-full text-[12px] font-semibold text-(--muted-color)`, label.class)}>{label.name}</span>
66
66
  {/if}
67
67
 
68
68
  {#if type === "button"}
69
- <!-- Иконочная кнопка с подсказкой, как UI.Button; выбор файла — скрытый input за ней -->
69
+ <!-- Иконочная кнопка с подсказкой, как UI.Button; выбор файла - скрытый input за ней -->
70
70
  <Button {componentClass} {disabled} content={{ icon: icon ?? OPEN_FILE_ICON, info }} onClick={triggerFileInput} />
71
71
  <input id={ID} type="file" class="hidden" {accept} {disabled} onchange={handleFileChange} />
72
72
  {:else if type === "image"}
@@ -2,7 +2,7 @@
2
2
  <script module lang="ts">
3
3
  import type { IOption } from "../types"
4
4
 
5
- /* Константы и настройки — экспортируются для переиспользования в GraphProps.svelte (редактор конструктора) */
5
+ /* Константы и настройки - экспортируются для переиспользования в GraphProps.svelte (редактор конструктора) */
6
6
  export const REFRESH_OPTIONS: IOption[] = [
7
7
  { id: "RefreshOption-AUTO", name: "AUTO", value: 0, class: "" },
8
8
  { id: "RefreshOption-50", name: "50", value: 50, class: "" },
@@ -72,7 +72,7 @@
72
72
  }
73
73
  }
74
74
 
75
- /* Тултип при наведении — время и значения всех серий в ближайшей к курсору точке */
75
+ /* Тултип при наведении - время и значения всех серий в ближайшей к курсору точке */
76
76
  let tooltip = $state<{ visible: boolean; x: number; y: number; time: number; items: { name: string; value: number; color: string }[] }>({
77
77
  visible: false,
78
78
  x: 0,
@@ -155,7 +155,7 @@
155
155
  drawAllGraphs()
156
156
  }, selectedRefreshRate)
157
157
  } else if (selectedRefreshRate == 0 && !isTest) {
158
- /* AUTO: streamingData не несёт признак "новый пакет" — опрашиваем каждые 10мс, рисуем точку только при реальном изменении значений */
158
+ /* AUTO: streamingData не несёт признак "новый пакет" - опрашиваем каждые 10мс, рисуем точку только при реальном изменении значений */
159
159
  intervalId = setInterval(() => {
160
160
  if (!streamingData.data || streamingData.data.length === 0) return
161
161
  const newValues = (streamingData.data as IGraphDataObject[]).map((dataset) => dataset.value)
@@ -198,7 +198,7 @@
198
198
  return () => resizeObserver.disconnect()
199
199
  })
200
200
 
201
- /* Общая геометрия осей — переиспользуется отрисовкой и обработчиком наведения мыши, чтобы не дублировать формулы */
201
+ /* Общая геометрия осей - переиспользуется отрисовкой и обработчиком наведения мыши, чтобы не дублировать формулы */
202
202
  const computeGeometry = () => {
203
203
  const padding = { top: 10, right: 10, bottom: 20, left: 35 }
204
204
  const graphWidth = width - padding.left - padding.right
@@ -209,7 +209,7 @@
209
209
  const maxX = allPoints.length ? Math.max(...allPoints.map((p) => p.x)) : 1
210
210
  const timeSpan = maxX - minX || 1
211
211
 
212
- /* Авто-масштаб по данным (±10%): статический — разово по массиву, живой — через сглаженный liveYRange (updateLiveYRange) */
212
+ /* Авто-масштаб по данным (±10%): статический - разово по массиву, живой - через сглаженный liveYRange (updateLiveYRange) */
213
213
  let yMin: number
214
214
  let yMax: number
215
215
  if (isHistoryMode) {
@@ -230,7 +230,7 @@
230
230
  return { padding, graphWidth, graphHeight, minX, maxX, timeSpan, yMin, yMax, ySpan, getX, getY }
231
231
  }
232
232
 
233
- /* Бинарный поиск ближайшей по времени точки — точки в graphData всегда отсортированы по x (по построению) */
233
+ /* Бинарный поиск ближайшей по времени точки - точки в graphData всегда отсортированы по x (по построению) */
234
234
  const findNearestPoint = (points: { x: number; y: number }[], t: number): { x: number; y: number } | null => {
235
235
  if (points.length === 0) return null
236
236
  let lo = 0
@@ -246,7 +246,7 @@
246
246
 
247
247
  const pad2 = (n: number) => String(n).padStart(2, "0")
248
248
 
249
- /* Компактная адаптивная подпись сетки по оси X: относительное время для живого режима, дата/время — для статического */
249
+ /* Компактная адаптивная подпись сетки по оси X: относительное время для живого режима, дата/время - для статического */
250
250
  const formatAxisLabel = (t: number, spanMs: number): string => {
251
251
  if (!isHistoryMode) return `${((t - Date.now()) / 1000).toFixed(0)}s`
252
252
  const d = new Date(t)
@@ -302,7 +302,7 @@
302
302
  drawAllGraphs()
303
303
  }
304
304
 
305
- /* Прозрачность линий сетки — общая с PeriodChart (border-(--faint-color)/40) */
305
+ /* Прозрачность линий сетки - общая с PeriodChart (border-(--faint-color)/40) */
306
306
  const GRID_ALPHA = 0.4
307
307
 
308
308
  const drawAllGraphs = () => {
@@ -390,7 +390,7 @@
390
390
  {/if}
391
391
 
392
392
  <div class="flex w-full min-w-0 flex-row gap-4">
393
- <!-- График: холст абсолютно поверх контейнера — его пиксельная ширина не распирает контейнер, иначе в сетке/flex с 1fr ширина растёт по кругу (ResizeObserver) -->
393
+ <!-- График: холст абсолютно поверх контейнера - его пиксельная ширина не распирает контейнер, иначе в сетке/flex с 1fr ширина растёт по кругу (ResizeObserver) -->
394
394
  <!-- design-handoff, раздел 9: рамка --hairline-color без тени, тултип в оформлении общего Tooltip.svelte -->
395
395
  <div bind:this={container} class="relative h-64 min-w-0 grow overflow-hidden rounded-[14px] border border-(--hairline-color)">
396
396
  <canvas class="absolute inset-0 h-full w-full bg-(--back-color)" bind:this={canvas} onmousemove={handleMouseMove} onmouseleave={handleMouseLeave}
@@ -425,7 +425,7 @@
425
425
  />
426
426
  {/if}
427
427
 
428
- <!-- Переменные и их значение: сетка, а не таблица — имя обрезается «…», значение не переносится -->
428
+ <!-- Переменные и их значение: сетка, а не таблица - имя обрезается «…», значение не переносится -->
429
429
  <div class="flex flex-col gap-1">
430
430
  <h5 class="text-[12px] font-semibold text-(--muted-color)">{$T("ui.graph.values")}</h5>
431
431
  <div class="grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-2 gap-y-1 text-[13px]">
@@ -45,7 +45,7 @@
45
45
  | `wrapperClass` | `string` | `""` | Классы обёртки |
46
46
  | `label.name`, `label.class` | `string` | `""` | Заголовок и его классы |
47
47
  | `streamingData.data` | `{ name, value }[] \| null` | `[]` | Текущие значения линий для живого режима |
48
- | `refreshRate` | `number` | `0` | Период добавления точек, мс. `0` — по изменению значений |
48
+ | `refreshRate` | `number` | `0` | Период добавления точек, мс. `0` - по изменению значений |
49
49
  | `historyData` | `{ name, points: { value, timestamp }[] }[]` | `[]` | Готовый ряд для статического режима. `timestamp` в миллисекундах, как `Date.now()` |
50
50
  | `isTest` | `boolean` | `false` | Режим имитации: случайные данные, точка каждые 50 мс |
51
51
 
@@ -1,4 +1,4 @@
1
- <!-- $lib/Input/Input.svelte — радиус 10, высота 32 (h-8), выравнивание по левому краю, кольцо фокуса. -->
1
+ <!-- $lib/Input/Input.svelte - радиус 10, высота 32 (h-8), выравнивание по левому краю, кольцо фокуса. -->
2
2
  <script lang="ts">
3
3
  import { T } from "../locales/i18n"
4
4
  import type { IInputProps } from "../types"
@@ -86,7 +86,7 @@
86
86
 
87
87
  <div class={twMerge(`relative flex w-full flex-col gap-1 ${type === "text-area" ? "h-full" : ""}`, wrapperClass)}>
88
88
  {#if label.name}
89
- <!-- Метка слева, 12/600 — вместо центрированного h5 -->
89
+ <!-- Метка слева, 12/600 - вместо центрированного h5 -->
90
90
  <span class={twMerge(`text-[12px] font-semibold text-(--muted-color)`, label.class)}>{label.name}</span>
91
91
  {/if}
92
92
 
@@ -33,14 +33,14 @@
33
33
  const initialAlign = $derived(findOptionByClass($optionsStore.TEXT_ALIGN_OPTIONS, component.properties.label?.class, "text-"))
34
34
 
35
35
  /* Обновление свойства */
36
- /* Подсказка битового режима «Максимальное число: N» сразу на всех языках — иначе записалась бы на языке интерфейса конструктора */
36
+ /* Подсказка битового режима «Максимальное число: N» сразу на всех языках - иначе записалась бы на языке интерфейса конструктора */
37
37
  const maxNumInfo = (range: { start: number; end: number }) => {
38
38
  const max = Math.pow(2, range.end - range.start + 1) - 1
39
39
  const row = translations["constructor.props.maxnum"]
40
40
  return Object.fromEntries(LANGUAGE_CODES.map((lang) => [lang, `${row[lang] || row[DEFAULT_LANGUAGE]}: ${max}`]))
41
41
  }
42
42
 
43
- /* Диапазон битов поля bitMode; без range в свойствах — один нулевой бит */
43
+ /* Диапазон битов поля bitMode; без range в свойствах - один нулевой бит */
44
44
  const bitRange = $derived(component.properties.range ?? { start: 0, end: 0 })
45
45
 
46
46
  const updateProperty = (path: string, value: string | object | boolean | number | RegExp) => {
@@ -105,7 +105,11 @@
105
105
  value={VARIABLE_OPTIONS.find((opt) => opt.value === component.properties.id)}
106
106
  onUpdate={(value) => {
107
107
  updateProperty("id", (value as IOption).value as string)
108
- onPropertyChange({ name: (value as IOption).name?.split("—")[1]?.trim(), eventHandler: { Variables: [(value as IOption).value as string] } })
108
+ /* \u041f\u043e\u0434\u043f\u0438\u0441\u044c \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u0438\u0437 \u043e\u0431\u043b\u0430\u043a\u0430 \u00ab<id> - <\u0438\u043c\u044f>\u00bb: \u0438\u043c\u044f - \u0432\u0441\u0451 \u043f\u043e\u0441\u043b\u0435 \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u0435\u043b\u044f; \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u0438 \u0434\u043b\u0438\u043d\u043d\u043e\u0435 \u0442\u0438\u0440\u0435 (\u2014) \u0441\u0442\u0430\u0440\u044b\u0445 \u043f\u043e\u0434\u043f\u0438\u0441\u0435\u0439 */
109
+ onPropertyChange({
110
+ name: (value as IOption).name?.match(/\s[\u2014-]\s(.*)/)?.[1]?.trim(),
111
+ eventHandler: { Variables: [(value as IOption).value as string] },
112
+ })
109
113
  }}
110
114
  />
111
115
  {/snippet}
@@ -202,7 +202,7 @@
202
202
  </div>
203
203
  </div>
204
204
 
205
- <!-- Кнопка 0 — 32 px по общему правилу кнопок-иконок (design-handoff, раздел 9) -->
205
+ <!-- Кнопка 0 - 32 px по общему правилу кнопок-иконок (design-handoff, раздел 9) -->
206
206
  <div class="absolute {label.name ? 'top-4' : 'top-0'} w-55 flex items-center justify-end">
207
207
  <button
208
208
  class="flex size-[32px] cursor-pointer items-center justify-center rounded-full border border-(--hairline-color) bg-(--back-color) p-0.5 text-[15px] font-semibold
package/dist/Modal.svelte CHANGED
@@ -1,4 +1,4 @@
1
- <!-- $lib/Modal.svelte — модальное окно: радиус 20, шапка и подвал через линии, подложка 40% с размытием, стек окон, закрытие по Escape и клику вне окна -->
1
+ <!-- $lib/Modal.svelte - модальное окно: радиус 20, шапка и подвал через линии, подложка 40% с размытием, стек окон, закрытие по Escape и клику вне окна -->
2
2
  <script lang="ts">
3
3
  import { T } from "./locales/i18n"
4
4
  import { type Snippet } from "svelte"
@@ -104,7 +104,7 @@
104
104
  </div>
105
105
 
106
106
  {#if footer}
107
- <!-- Подтверждающее действие — крайнее справа; row-reverse сохранён, поэтому порядок сниппетов в вызовах менять не нужно -->
107
+ <!-- Подтверждающее действие - крайнее справа; row-reverse сохранён, поэтому порядок сниппетов в вызовах менять не нужно -->
108
108
  <div class="flex flex-row-reverse items-center justify-start gap-2 border-t border-(--hairline-color) bg-(--container-color) px-5 py-3">
109
109
  {@render footer?.()}
110
110
  </div>
@@ -1,10 +1,10 @@
1
- <!-- $lib/PeriodChart/PeriodChart.svelte — столбики за период и сегментированный переключатель периода, акцент только у выбранного -->
1
+ <!-- $lib/PeriodChart/PeriodChart.svelte - столбики за период и сегментированный переключатель периода, акцент только у выбранного -->
2
2
  <script module lang="ts">
3
3
  import type { IPeriodChartLevel } from "../types"
4
4
 
5
5
  const randomLevelData = (n: number) => Array.from({ length: n }, () => Math.round(Math.random() * 100))
6
6
 
7
- /* Демо-уровни, когда levels не заданы; name — ключ перевода */
7
+ /* Демо-уровни, когда levels не заданы; name - ключ перевода */
8
8
  export const DEFAULT_LEVELS: IPeriodChartLevel[] = [
9
9
  { name: "ui.period.year", data: randomLevelData(12) },
10
10
  { name: "ui.period.month", data: randomLevelData(31) },
@@ -51,7 +51,7 @@
51
51
  return { step, top: step * count, ticks: Array.from({ length: count + 1 }, (_, i) => step * (count - i)) }
52
52
  })
53
53
 
54
- /* Подпись деления: k/M для больших значений, число знаков после запятой — по величине шага */
54
+ /* Подпись деления: k/M для больших значений, число знаков после запятой - по величине шага */
55
55
  const formatTick = (v: number) => {
56
56
  const [div, suffix] = axis.top >= 1e6 ? [1e6, "M"] : axis.top >= 1e4 ? [1e3, "k"] : [1, ""]
57
57
  const step = axis.step / div
@@ -47,7 +47,7 @@
47
47
  .filter((s) => s.length > 0)
48
48
  const formatStrings = (arr: string[] | undefined): string => (arr || []).join(", ")
49
49
 
50
- /* eventHandler.Variables — список всех PWR.*-переменных со всех уровней, чтобы рантайм подписался на них разом */
50
+ /* eventHandler.Variables - список всех PWR.*-переменных со всех уровней, чтобы рантайм подписался на них разом */
51
51
  const updateLevels = (levels: IPeriodChartLevel[]) => {
52
52
  updateProperty("levels", levels, component, onPropertyChange)
53
53
  onPropertyChange({ eventHandler: { Variables: levels.map((l) => l.variable).filter((v): v is string => !!v) } })
@@ -1,4 +1,4 @@
1
- <!-- $lib/ProgressBar/ProgressBar.svelte — полоса 6px, подпись и значение над ней -->
1
+ <!-- $lib/ProgressBar/ProgressBar.svelte - полоса 6px, подпись и значение над ней -->
2
2
  <script lang="ts">
3
3
  import { twMerge } from "tailwind-merge"
4
4
  import type { IProgressBarProps, IReceivingDataObject } from "../types"
@@ -1,4 +1,4 @@
1
- <!-- $lib/PropsGroup.svelte — карточка-группа полей для панелей свойств конструктора (*Props.svelte). Визуальный язык взят из уже принятого вложенного оформления смарт-виджетов (WidgetWiFi.svelte/WidgetDeviceInfo.svelte: rounded-[10px] border bg-(--back-color) p-3 + подпись text-[12px] font-semibold text-(--muted-color)) — общий premium-вид панелей вместо голых flex-колонок без оформления. -->
1
+ <!-- $lib/PropsGroup.svelte - карточка-группа полей для панелей свойств конструктора (*Props.svelte). Визуальный язык взят из уже принятого вложенного оформления смарт-виджетов (WidgetWiFi.svelte/WidgetDeviceInfo.svelte: rounded-[10px] border bg-(--back-color) p-3 + подпись text-[12px] font-semibold text-(--muted-color)) - общий premium-вид панелей вместо голых flex-колонок без оформления. -->
2
2
  <script lang="ts">
3
3
  import { twMerge } from "tailwind-merge"
4
4
  import type { Snippet } from "svelte"
@@ -1,4 +1,4 @@
1
- <!-- $lib/PropsListHeader.svelte — подписи колонок над однострочными элементами списка в окнах свойств (вместо подписи у каждого поля); gridClass — тот же шаблон колонок, что у строк списка, пустая подпись — колонка без заголовка (ручка, удаление) -->
1
+ <!-- $lib/PropsListHeader.svelte - подписи колонок над однострочными элементами списка в окнах свойств (вместо подписи у каждого поля); gridClass - тот же шаблон колонок, что у строк списка, пустая подпись - колонка без заголовка (ручка, удаление) -->
2
2
  <script lang="ts">
3
3
  let { gridClass, captions }: { gridClass: string; captions: string[] } = $props()
4
4
  </script>
@@ -1,4 +1,4 @@
1
- <!-- $lib/PropsListModal.svelte — вынос "тяжёлых" списковых секций панелей свойств (столбцы Table, опции ProgressBar/Select/Switch/Tabs, уровни PeriodChart) в модалку вместо попытки втиснуть их в узкий сайдбар конструктора (ConstructorUI.svelte). Кнопка-триггер показывает счётчик элементов. -->
1
+ <!-- $lib/PropsListModal.svelte - вынос "тяжёлых" списковых секций панелей свойств (столбцы Table, опции ProgressBar/Select/Switch/Tabs, уровни PeriodChart) в модалку вместо попытки втиснуть их в узкий сайдбар конструктора (ConstructorUI.svelte). Кнопка-триггер показывает счётчик элементов. -->
2
2
  <script lang="ts">
3
3
  import { T } from "./locales/i18n"
4
4
  import type { Snippet } from "svelte"
@@ -1,4 +1,4 @@
1
- <!-- $lib/Select/Select.svelte — радиус 10, высота 36, выравнивание по левому краю, список — единственный элемент с тенью, группа type="buttons" без разрывов. -->
1
+ <!-- $lib/Select/Select.svelte - радиус 10, высота 36, выравнивание по левому краю, список - единственный элемент с тенью, группа type="buttons" без разрывов. -->
2
2
  <script lang="ts" generics="T = unknown">
3
3
  import { slide } from "svelte/transition"
4
4
  import { onMount } from "svelte"
@@ -14,7 +14,7 @@
14
14
  let portalElement: HTMLDivElement | undefined = $state()
15
15
  let anchorRect: { top: number; bottom: number; left: number; width: number } | null = $state(null)
16
16
 
17
- /* Список вариантов выносится в document.body (портал), чтобы его не обрезал overflow-hidden/overflow-auto ближайшего скроллящегося предка (например, панель свойств конструктора). Позиция считается от dropdownElement и обновляется, пока список открыт, при скролле/резайзе где угодно на странице. Атрибут data-ui-portal на контейнере — чтобы Modal.svelte не принимал клик по опции (физически лежащей вне [data-modal]) за клик "снаружи" и не закрывался. */
17
+ /* Список вариантов выносится в document.body (портал), чтобы его не обрезал overflow-hidden/overflow-auto ближайшего скроллящегося предка (например, панель свойств конструктора). Позиция считается от dropdownElement и обновляется, пока список открыт, при скролле/резайзе где угодно на странице. Атрибут data-ui-portal на контейнере - чтобы Modal.svelte не принимал клик по опции (физически лежащей вне [data-modal]) за клик "снаружи" и не закрывался. */
18
18
  function portal(node: HTMLElement) {
19
19
  document.body.appendChild(node)
20
20
  return {
@@ -124,7 +124,7 @@
124
124
  onUpdate?.(selectedOption)
125
125
  }
126
126
 
127
- /* Общая база поля — та же, что у Input */
127
+ /* Общая база поля - та же, что у Input */
128
128
  const fieldBase = `h-8 w-full rounded-[10px] border border-(--border-color) bg-(--field-color) px-3 text-left text-[14px]
129
129
  transition-[border-color,box-shadow] duration-150 outline-none
130
130
  focus:border-(--accent-color) focus:shadow-(--focus-shadow-color)`
@@ -209,7 +209,7 @@
209
209
  {@const selected = value !== null && isSelected(option)}
210
210
  {@const hasOwnColor = !!option.class?.split(/\s+/).find((c) => c.startsWith("bg-"))}
211
211
  <!-- Заливка вне twMerge, чтобы не конфликтовать с bg-* из option.class: опции со своим цветом показывают его всегда и отмечаются рамкой, обычный сегмент-контрол заливает только выбранный пункт -->
212
- <!-- Обёртка — позиционирующий предок подсказки; без overflow-hidden у группы, чтобы подсказка не обрезалась -->
212
+ <!-- Обёртка - позиционирующий предок подсказки; без overflow-hidden у группы, чтобы подсказка не обрезалась -->
213
213
  <div class="relative flex {fitContent ? '' : 'min-w-0 flex-1'}">
214
214
  <button
215
215
  id={option.id}
@@ -44,7 +44,7 @@
44
44
  : ($optionsStore.SELECT_VALUE_TYPE_OPTIONS[0] as ValueTypeOption),
45
45
  )
46
46
 
47
- /* Диапазон битов bitMode; без range в свойствах — один нулевой бит */
47
+ /* Диапазон битов bitMode; без range в свойствах - один нулевой бит */
48
48
  const bitRange = $derived(component.properties.range ?? { start: 0, end: 0 })
49
49
 
50
50
  let Header: IOption = $derived(
@@ -65,7 +65,7 @@
65
65
  ),
66
66
  )
67
67
 
68
- /* Argument пуст — поле ниже лишь визуально показывает "ModCfg", реальное значение нужно проставить в состояние */
68
+ /* Argument пуст - поле ниже лишь визуально показывает "ModCfg", реальное значение нужно проставить в состояние */
69
69
  $effect(() => {
70
70
  if (!component.eventHandler?.Argument) onPropertyChange({ eventHandler: { Argument: "ModCfg" } })
71
71
  })
@@ -1,4 +1,4 @@
1
- <!-- $lib/Slider/Slider.svelte — дорожка 4px, ползунок 18px; в диапазонном режиме оба <input> на всю ширину с одинаковыми min/max наложены друг на друга, события ловит только ::thumb, клик по пустой дорожке ползунок не двигает -->
1
+ <!-- $lib/Slider/Slider.svelte - дорожка 4px, ползунок 18px; в диапазонном режиме оба <input> на всю ширину с одинаковыми min/max наложены друг на друга, события ловит только ::thumb, клик по пустой дорожке ползунок не двигает -->
2
2
  <script lang="ts">
3
3
  import { T } from "../locales/i18n"
4
4
  import type { ISliderProps } from "../types"
@@ -6,7 +6,7 @@
6
6
 
7
7
  let {
8
8
  id = crypto.randomUUID(),
9
- /* Было "bg-blue" — декоративная заливка. Цвет заливки дорожки берётся из --bg-color, который задаётся классом bg-* в вызове; без него используется акцент. */
9
+ /* Было "bg-blue" - декоративная заливка. Цвет заливки дорожки берётся из --bg-color, который задаётся классом bg-* в вызове; без него используется акцент. */
10
10
  wrapperClass = "",
11
11
  label = { name: "", class: "" },
12
12
  type = "single",
@@ -25,7 +25,7 @@
25
25
  let lowerValue = $derived(isRange && Array.isArray(value) ? value[0] : number.minNum)
26
26
  let upperValue = $derived(isRange && Array.isArray(value) ? value[1] : number.maxNum)
27
27
 
28
- /* Позиция значения в процентах — для отрисовки заливки */
28
+ /* Позиция значения в процентах - для отрисовки заливки */
29
29
  const pct = (v: number) => {
30
30
  const span = Math.abs((number.maxNum ?? 10) - (number.minNum ?? 0)) || 1
31
31
  return Math.max(0, Math.min(100, ((v - (number.minNum ?? 0)) / span) * 100))
@@ -71,8 +71,8 @@
71
71
  return Number(num.toFixed(2))
72
72
  }
73
73
 
74
- /* Единые классы для нативного ползунка: высота input РАВНА высоте thumb (18px) — тогда центрирование по вертикали отдаётся обычному flexbox родителя (items-center), без ручного margin-top на ::-webkit-slider-thumb. Такой margin-top пришлось бы подбирать под конкретную высоту трека, и он даёт разный результат в Chromium и Safari/WebKit — при равных высотах input и thumb смещения нет по определению, это работает одинаково везде. */
75
- /* Бегунок: было border-(--hairline-color) (почти прозрачная линия) + мягкая тень — на светлом фоне белый кружок буквально сливался с дорожкой. Рамка в 2px цветом заливки даёт чёткий, "премиальный" контур вместо почти невидимой линии. */
74
+ /* Единые классы для нативного ползунка: высота input РАВНА высоте thumb (18px) - тогда центрирование по вертикали отдаётся обычному flexbox родителя (items-center), без ручного margin-top на ::-webkit-slider-thumb. Такой margin-top пришлось бы подбирать под конкретную высоту трека, и он даёт разный результат в Chromium и Safari/WebKit - при равных высотах input и thumb смещения нет по определению, это работает одинаково везде. */
75
+ /* Бегунок: было border-(--hairline-color) (почти прозрачная линия) + мягкая тень - на светлом фоне белый кружок буквально сливался с дорожкой. Рамка в 2px цветом заливки даёт чёткий, "премиальный" контур вместо почти невидимой линии. */
76
76
  const THUMB = `w-full appearance-none bg-transparent h-[18px]
77
77
  [&::-webkit-slider-runnable-track]:h-[18px] [&::-webkit-slider-runnable-track]:bg-transparent
78
78
  [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-[18px]
@@ -85,7 +85,7 @@
85
85
  [&::-moz-range-thumb]:shadow-[0_1px_4px_rgb(16_24_40/0.35)]
86
86
  focus-visible:outline-none`
87
87
 
88
- /* Двойной слайдер: оба input на всю ширину трека с ОДИНАКОВЫМ min/max, наложены друг на друга. pointer-events выключен на самом input и включён только на ::thumb (класс .range-overlay ниже) — тянуть можно исключительно за конкретный бегунок. Tailwind не генерирует CSS для сочетания произвольного варианта псевдоэлемента с pointer-events-*, поэтому это обычный style-блок ниже, а не класс. */
88
+ /* Двойной слайдер: оба input на всю ширину трека с ОДИНАКОВЫМ min/max, наложены друг на друга. pointer-events выключен на самом input и включён только на ::thumb (класс .range-overlay ниже) - тянуть можно исключительно за конкретный бегунок. Tailwind не генерирует CSS для сочетания произвольного варианта псевдоэлемента с pointer-events-*, поэтому это обычный style-блок ниже, а не класс. */
89
89
  </script>
90
90
 
91
91
  <div class={twMerge(`relative flex w-full flex-col gap-1.5`, wrapperClass)}>
@@ -96,7 +96,7 @@
96
96
  <div class="flex w-full items-center gap-3">
97
97
  <!-- Дорожка -->
98
98
  <div id={`${id}-${crypto.randomUUID().slice(0, 6)}`} class="relative flex h-8 flex-1 items-center {disabled ? 'cursor-not-allowed opacity-45' : ''}">
99
- <!-- Фон дорожки 6px — --container-color здесь давал слишком слабый контраст с фоном страницы (#f0f2f5 на белом), дорожка визуально пропадала; --border-color заметно контрастнее и совпадает с рамкой счётчика ниже. -->
99
+ <!-- Фон дорожки 6px - --container-color здесь давал слишком слабый контраст с фоном страницы (#f0f2f5 на белом), дорожка визуально пропадала; --border-color заметно контрастнее и совпадает с рамкой счётчика ниже. -->
100
100
  <div class="pointer-events-none absolute inset-x-0 top-1/2 h-1.5 -translate-y-1/2 rounded-full bg-(--border-color)"></div>
101
101
  <!-- Заливка -->
102
102
  {#if isRange}
@@ -218,7 +218,7 @@
218
218
  </div>
219
219
 
220
220
  <style>
221
- /* Двойной слайдер: оба input наложены на всю ширину трека, дорожка каждого не должна перехватывать клик — только его собственный thumb. */
221
+ /* Двойной слайдер: оба input наложены на всю ширину трека, дорожка каждого не должна перехватывать клик - только его собственный thumb. */
222
222
  .range-overlay {
223
223
  pointer-events: none;
224
224
  }
@@ -1,4 +1,4 @@
1
- <!-- $lib/Switch/Switch.svelte — тумблер, геометрия считается от пропа height; чекбокс 20px с радиусом 6 -->
1
+ <!-- $lib/Switch/Switch.svelte - тумблер, геометрия считается от пропа height; чекбокс 20px с радиусом 6 -->
2
2
  <script lang="ts">
3
3
  import { twMerge } from "tailwind-merge"
4
4
  import type { ISwitchProps } from "../types"
@@ -58,7 +58,7 @@
58
58
  {#if type !== "checkbox"}
59
59
  <div class={twMerge(`relative flex w-full flex-col items-center justify-center gap-1.5`, wrapperClass)}>
60
60
  {#if label.name}
61
- <!-- Подпись по умолчанию по центру — над переключателем, который тоже по центру; выравнивание из label.class важнее -->
61
+ <!-- Подпись по умолчанию по центру - над переключателем, который тоже по центру; выравнивание из label.class важнее -->
62
62
  <h5 class={twMerge(`w-full text-center text-[12px] font-semibold text-(--muted-color)`, label.class)}>{label.name}</h5>
63
63
  {/if}
64
64
  <div class="flex w-full {type == 'vertical' ? 'flex-wrap' : ''} items-center justify-around gap-4">
@@ -90,7 +90,7 @@
90
90
  disabled={option.disabled}
91
91
  onchange={() => handleToggle(index)}
92
92
  />
93
- <!-- Дорожка: акцент во включённом состоянии, нейтральная серая в выключенном. Фолбэк var(--accent-color) — большинство вызовов Switch не передают wrapperClass с ролью цвета (bg-blue и т.п.), без фолбэка --bg-color остаётся не задан и дорожка становится невидимой при включении. -->
93
+ <!-- Дорожка: акцент во включённом состоянии, нейтральная серая в выключенном. Фолбэк var(--accent-color) - большинство вызовов Switch не передают wrapperClass с ролью цвета (bg-blue и т.п.), без фолбэка --bg-color остаётся не задан и дорожка становится невидимой при включении. -->
94
94
  <span
95
95
  class="relative flex items-center rounded-full transition-colors duration-200
96
96
  {checkedOptions[index] ? 'bg-(--bg-color,var(--accent-color))' : 'bg-(--border-color)'}
@@ -98,7 +98,7 @@
98
98
  {/if}
99
99
  {#each component.properties.options || [] as option, index (option.id)}
100
100
  <div class="grid {BIT_GRID} items-center gap-3 rounded-lg border border-(--hairline-color) bg-(--container-color)/60 px-2 py-1.5">
101
- <!-- Имя варианта — короткая метка бита (до 4 символов) в битовом режиме, не переводится -->
101
+ <!-- Имя варианта - короткая метка бита (до 4 символов) в битовом режиме, не переводится -->
102
102
  <UI.Input
103
103
  value={option.name}
104
104
  maxlength={4}