@sberbusiness/triplex-next-mcp-server 1.41.0-beta → 1.43.0-beta

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 (41) hide show
  1. package/bundle/components/AlertProcess.md +460 -0
  2. package/bundle/components/Amount.md +145 -169
  3. package/bundle/components/Badge.md +155 -219
  4. package/bundle/components/Body.md +108 -116
  5. package/bundle/components/Button.md +1 -0
  6. package/bundle/components/CarouselExtended.md +355 -194
  7. package/bundle/components/Checkbox.md +7 -1
  8. package/bundle/components/CheckboxTreeExtended.md +149 -248
  9. package/bundle/components/CheckboxXGroup.md +2 -0
  10. package/bundle/components/CheckboxYGroup.md +7 -2
  11. package/bundle/components/ChipGroup.md +195 -126
  12. package/bundle/components/ComposedKeyDownListener.md +323 -0
  13. package/bundle/components/FooterPage.md +2 -1
  14. package/bundle/components/HeaderPage.md +2 -1
  15. package/bundle/components/Island.md +100 -12
  16. package/bundle/components/KeyDownListener.md +11 -72
  17. package/bundle/components/LightBox.md +6 -0
  18. package/bundle/components/LoaderSmall.md +230 -0
  19. package/bundle/components/MaskedField.md +481 -0
  20. package/bundle/components/MediaWidth.md +252 -108
  21. package/bundle/components/MobileView.md +192 -68
  22. package/bundle/components/ModalWindow.md +3 -1
  23. package/bundle/components/Notification.md +21 -3
  24. package/bundle/components/Page.md +3 -1
  25. package/bundle/components/Pagination.md +20 -0
  26. package/bundle/components/SelectExtendedField.md +691 -236
  27. package/bundle/components/SliderExtended.md +358 -222
  28. package/bundle/components/TabsExtended.md +380 -194
  29. package/bundle/components/UnorderedList.md +254 -0
  30. package/bundle/components/UnorderedListExtended.md +255 -0
  31. package/bundle/components/WindowResizeListener.md +249 -0
  32. package/bundle/guides/commits.md +66 -3
  33. package/bundle/guides/context.md +26 -0
  34. package/bundle/guides/stories.md +3 -1
  35. package/bundle/guides/template.md +1 -1
  36. package/bundle/manifest.json +64 -21
  37. package/bundle/release-notes/1.41.1.mdx +18 -0
  38. package/bundle/release-notes/1.42.0.mdx +81 -0
  39. package/bundle/release-notes/1.43.0.mdx +94 -0
  40. package/bundle/release-notes/1.44.0.mdx +7 -0
  41. package/package.json +1 -1
@@ -0,0 +1,249 @@
1
+ ---
2
+ component: WindowResizeListener
3
+ category: WindowResizeListener
4
+ related: [MediaWidth]
5
+ tokens: []
6
+ stories: stories/WindowResizeListener/WindowResizeListener.stories.tsx
7
+ version: "1.0"
8
+ ---
9
+
10
+ # WindowResizeListener
11
+
12
+ ## Назначение
13
+
14
+ Сервисный headless-компонент. Пока смонтирован, слушает событие `resize` на
15
+ `window` и вызывает `onResize`, но не чаще, чем раз в `throttleDelay`
16
+ миллисекунд. Собственной разметки не рендерит: `children` выводятся как есть,
17
+ без обёртки; без `children` не рендерит ничего.
18
+
19
+ Используй когда: при изменении размеров окна нужно выполнить императивное
20
+ действие — пересчитать размеры/отступы по замеренному DOM, перепозиционировать
21
+ оверлей, сбросить кэш измерений.
22
+
23
+ Не используй когда:
24
+ - Нужно отрендерить **другое дерево** компонентов на другой ширине — для этого
25
+ есть `MediaWidth` / `MobileView`, они слушают `MediaQueryList`, а не поток
26
+ событий `resize`.
27
+ - Нужно булево значение «широкий/узкий экран» для props или условной разметки —
28
+ используй `useMatchMedia` (`src/components/MediaWidth/useMatchMedia.ts`) или
29
+ `useMobileView`.
30
+ - Отслеживать нужно размер конкретного элемента, а не окна — окно может не
31
+ меняться, а элемент — да (открылась панель, изменился контент). Для этого в
32
+ библиотеке используется `useResizeDetector` из `react-resize-detector`
33
+ (пример — `LightBoxContent`).
34
+ - Разница чисто визуальная (отступы, шрифт, раскладка) — это дешевле решается
35
+ медиа-запросом в `*.module.less`, без JS-обработчика на каждое событие.
36
+
37
+ ---
38
+
39
+ ## Варианты и props
40
+
41
+ ### Обязательные props
42
+
43
+ | Prop | Тип | Описание |
44
+ |---|---|---|
45
+ | `onResize` | `(event: UIEvent) => void` | Вызывается на `resize` окна. Получает нативный `UIEvent` (не React SyntheticEvent). Полезной нагрузки в событии нет — актуальные размеры читаются из `window.innerWidth` / `innerHeight` или замером DOM |
46
+
47
+ ### Опциональные props
48
+
49
+ | Prop | Тип | По умолчанию | Описание |
50
+ |---|---|---|---|
51
+ | `throttleDelay` | `number` | `100` | Минимальный интервал между вызовами `onResize`, в миллисекундах |
52
+ | `children` | `React.ReactNode` | — | Рендерится как есть. Компонент не оборачивает содержимое и никак на него не влияет — вложение нужно только для читаемости кода |
53
+
54
+ ### Особенности поведения
55
+
56
+ - Слушатель навешивается в `useEffect`, поэтому `window` не трогается во время
57
+ рендера — компонент **SSR-safe** (в отличие от семейства `MediaWidth`, которое
58
+ вызывает `window.matchMedia` на первом рендере).
59
+ - Троттлинг — `throttle` из `lodash-es` с настройками по умолчанию:
60
+ **leading + trailing**. Первое событие серии обрабатывается синхронно, все
61
+ последующие внутри окна схлопываются в один отложенный вызов в конце окна.
62
+ Одиночное событие даёт ровно один вызов; если внутри окна пришли ещё события,
63
+ в конце окна добавляется trailing-вызов.
64
+ - **Обработчик и задержка входят в зависимости эффекта.** При смене ссылки
65
+ `onResize` или значения `throttleDelay` слушатель снимается и навешивается
66
+ заново, а троттлинг начинается с чистого листа (новое leading-окно). Если
67
+ передать инлайновую стрелку, переподписка будет происходить на **каждый
68
+ рендер**, и троттлинг фактически перестанет ограничивать частоту вызовов.
69
+ Оборачивай обработчик в `useCallback` (или храни его вне компонента).
70
+ - При размонтировании слушатель снимается, а отложенный trailing-вызов
71
+ отменяется через `cancel()` — `removeEventListener` сам по себе его
72
+ не отменяет, и колбэк сработал бы уже после размонтирования.
73
+ - Компонент не вызывает `preventDefault` / `stopPropagation` (событие `resize`
74
+ не отменяемо) и не подмешивает в `onResize` собственные данные о размерах.
75
+ - Несколько смонтированных `WindowResizeListener` работают независимо, каждый со
76
+ своим окном троттлинга.
77
+
78
+ ---
79
+
80
+ ## Дизайн-токены
81
+
82
+ Собственных стилей и CSS-переменных нет — компонент не рендерит разметку.
83
+
84
+ ---
85
+
86
+ ## Инварианты
87
+
88
+ - **Без `forwardRef` — осознанное исключение** из общего правила библиотеки:
89
+ компонент не рендерит собственный host-элемент, форвардить `ref` некуда. Не
90
+ добавлять и не убирать без отдельного решения.
91
+ - **Имена и типы props `children`, `onResize`, `throttleDelay`** и значение по
92
+ умолчанию `throttleDelay = 100` — публичное API, не менять. Интерфейс
93
+ `IWindowResizeListenerProps` экспортируется из barrel — переименование будет
94
+ breaking change.
95
+ - **`return children ?? null`** — falsy-`children` (`0`, `""`) осознанно
96
+ рендерятся; `??`, а не `||` (у соседнего `KeyDownListener` обратная
97
+ договорённость — там `||`). Возврат `null` вместо `undefined` нужен для React
98
+ 17: там компонент, вернувший `undefined`, роняет рендер.
99
+ - **Зависимости эффекта `[onResize, throttleDelay]`** — не «оптимизировать»
100
+ переносом обработчика в `ref`: сейчас смена обработчика сбрасывает окно
101
+ троттлинга, и это наблюдаемое поведение у потребителей.
102
+ - **`throttledResize.cancel()` в cleanup** — не удалять: без него отложенный вызов
103
+ срабатывает после размонтирования (тест `cancels the pending trailing call on
104
+ unmount`).
105
+ - Реализация остаётся React 17-совместимой (без `useId`,
106
+ `useSyncExternalStore` и прочих React 18-only API): код синхронизируется
107
+ в `release-0`.
108
+
109
+ ---
110
+
111
+ ## Accessibility
112
+
113
+ - Собственной ARIA-семантики нет — компонент прозрачен для accessibility-дерева
114
+ и не рендерит фокусируемых элементов.
115
+ - `resize` окна возникает и при зуме страницы, и при появлении экранной
116
+ клавиатуры на мобильных. Обработчик не должен перебрасывать фокус или
117
+ сбрасывать состояние формы — для пользователя, увеличившего масштаб, это
118
+ выглядит как потеря контекста.
119
+
120
+ ---
121
+
122
+ ## Связанные компоненты
123
+
124
+ - `MediaWidth` (`src/components/MediaWidth/`) — альтернатива для декларативного
125
+ случая: рендерит одну из двух веток по медиа-запросу, а не даёт колбэк на поток
126
+ событий. Там же `useMatchMedia` (булево значение для условной разметки) и
127
+ `MobileView` (частный случай мобильной границы). Правило выбора: нужно
128
+ **что-то посчитать/сделать** на ресайз — `WindowResizeListener`; нужно
129
+ **что-то показать** на определённой ширине — `MediaWidth`.
130
+ - `KeyDownListener` (`src/components/KeyDownListener/`) — headless-слушатель того
131
+ же покроя, но для `keydown` на `window`. Похожий контракт по `children` (рендер
132
+ как есть, без обёртки); отличие — обработка falsy-`children`, см. «Инварианты».
133
+
134
+ ### Потребители внутри библиотеки
135
+
136
+ - `LightBoxContent` (`src/components/LightBox/LightBoxContent.tsx`) — пересчёт
137
+ верхнего отступа контента при изменении размеров окна. Обработчик
138
+ (`updateStyleWithTimeout`) обёрнут в `useCallback`, поэтому переподписки на
139
+ каждый рендер не происходит. Слушатель размеров самого контента там сделан
140
+ отдельно, через `useResizeDetector`.
141
+
142
+ В unit-тестах потребителей компонент удобно мокать целиком — как в
143
+ `src/components/LightBox/__tests__/LightBoxContent.test.tsx`.
144
+
145
+ ---
146
+
147
+ ## Stories
148
+
149
+ Основные истории: `stories/WindowResizeListener/WindowResizeListener.stories.tsx`
150
+ Файлы примеров: `stories/WindowResizeListener/examples/`
151
+
152
+ | Story | Example file | Что демонстрирует |
153
+ |---|---|---|
154
+ | `Playground` | `Playground.tsx` | Интерактивный подбор `throttleDelay`, счётчик срабатываний `onResize` и текущая ширина окна |
155
+ | `Default` | `Default.tsx` | Минимальный пример: обработчик в `useCallback` читает `window.innerWidth` / `innerHeight` |
156
+ | `ThrottleDelay` | `ThrottleDelay.tsx` | Два слушателя с разной задержкой (100 мс и 1000 мс) и их счётчики вызовов |
157
+
158
+ Набор целиком исключён из скриншот-тестов (`testRunner: { skip: true }` в `meta`):
159
+ собственной разметки компонент не рендерит, на скриншот попадала бы только
160
+ обвязка примеров. Baseline-файлов в `__screenshots__/` для этого набора быть не
161
+ должно. Поведение слушателя покрыто unit-тестами.
162
+
163
+ ---
164
+
165
+ ## История изменений
166
+
167
+ | Дата | Изменение |
168
+ |---|---|
169
+ | 2026-08-18 | Создан документ. AI-рефакторинг (TRI-104): JSDoc на props и компоненте, интерфейс `IWindowResizeListenerProps` стал экспортируемым (аддитивно), `return children ?? null` вместо `return children` (React 17-safe), убрано лишнее опциональное обращение `cancel?.()`, добавлены unit-тесты (10 кейсов: рендер `children`, троттлинг с дефолтной и заданной задержкой, отписка и отмена trailing-вызова при размонтировании, переподписка при смене `onResize` / `throttleDelay`) и набор stories по modern pattern. Публичный API не изменён (имена и типы props, дефолт `throttleDelay = 100`, barrel-экспорты; `forwardRef` по-прежнему осознанно отсутствует). |
170
+
171
+ ---
172
+
173
+ ## Примеры использования
174
+
175
+ ### Default
176
+
177
+ `stories/WindowResizeListener/examples/Default.tsx`
178
+
179
+ ```tsx
180
+ import React, { useCallback, useState } from "react";
181
+ import { WindowResizeListener } from "@sberbusiness/triplex-next";
182
+
183
+ const getWindowSize = () =>
184
+ typeof window === "undefined" ? { width: 0, height: 0 } : { width: window.innerWidth, height: window.innerHeight };
185
+
186
+ export const Default = () => {
187
+ // При рендере на сервере window недоступен — стартовое значение берётся только в браузере.
188
+ const [windowSize, setWindowSize] = useState(getWindowSize);
189
+
190
+ // Стабильная ссылка на обработчик: при её смене слушатель переподписывается заново.
191
+ const handleResize = useCallback(() => {
192
+ setWindowSize(getWindowSize());
193
+ }, []);
194
+
195
+ return (
196
+ <WindowResizeListener onResize={handleResize}>
197
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px", maxWidth: "400px" }}>
198
+ <div>Измените размер окна браузера, чтобы обновить значения.</div>
199
+ <div style={{ border: "1px dashed rgb(125, 131, 138)", borderRadius: "4px", padding: "16px" }}>
200
+ Размер окна: {windowSize.width} × {windowSize.height}
201
+ </div>
202
+ </div>
203
+ </WindowResizeListener>
204
+ );
205
+ };
206
+ ```
207
+
208
+ ### ThrottleDelay
209
+
210
+ `stories/WindowResizeListener/examples/ThrottleDelay.tsx`
211
+
212
+ ```tsx
213
+ import React, { useCallback, useState } from "react";
214
+ import { WindowResizeListener } from "@sberbusiness/triplex-next";
215
+
216
+ interface IResizeCounterProps {
217
+ throttleDelay: number;
218
+ }
219
+
220
+ const ResizeCounter = ({ throttleDelay }: IResizeCounterProps) => {
221
+ const [resizeCount, setResizeCount] = useState(0);
222
+
223
+ const handleResize = useCallback(() => {
224
+ setResizeCount((prevCount) => prevCount + 1);
225
+ }, []);
226
+
227
+ return (
228
+ <WindowResizeListener onResize={handleResize} throttleDelay={throttleDelay}>
229
+ <div>
230
+ <div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>
231
+ throttleDelay: {throttleDelay}ms
232
+ </div>
233
+ <div style={{ border: "1px dashed rgb(125, 131, 138)", borderRadius: "4px", padding: "16px" }}>
234
+ Срабатываний onResize: {resizeCount}
235
+ </div>
236
+ </div>
237
+ </WindowResizeListener>
238
+ );
239
+ };
240
+
241
+ export const ThrottleDelay = () => (
242
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px", maxWidth: "400px" }}>
243
+ <div>Измените размер окна браузера — чем больше задержка, тем реже вызывается обработчик.</div>
244
+ <ResizeCounter throttleDelay={100} />
245
+ <ResizeCounter throttleDelay={1000} />
246
+ </div>
247
+ );
248
+ ```
249
+
@@ -203,6 +203,32 @@ fi
203
203
  - Действия в Linear выполняются от сессии разработчика — бот на них
204
204
  не распространяется.
205
205
 
206
+ ### Облачные сессии: подмена токена прокси
207
+
208
+ В облачных сессиях (routines на claude.ai) запросы к `api.github.com`
209
+ идут **не** с `TRIPLEX_BOT_GH_TOKEN`: egress-прокси подменяет авторизацию
210
+ на токен GitHub App `claude` (user-to-server, identity — аккаунт
211
+ разработчика). Паттерн выше при этом не ломается — просто действующие
212
+ права определяет прокси, а не PAT. Как распознать:
213
+
214
+ - `gh api user` возвращает аккаунт разработчика, хотя в env лежит токен
215
+ бота;
216
+ - текст 403 — «Resource not accessible by **integration**» или «not
217
+ permitted for this session type / through this proxy» (у fine-grained
218
+ PAT было бы «…by personal access token»).
219
+
220
+ Что прокси **разрешает** из облака: push в ветки, создание и мерж PR,
221
+ комментарии, чтение Actions, диспатч нерелизных workflow (например,
222
+ `visual-update.yml` — подтверждено прогонами). Что **запрещает** (зонд
223
+ TRI-117, 2026-08-14): создание релизов, диспатч `release.yml`,
224
+ `repository_dispatch`, создание, push и удаление тегов, удаление веток —
225
+ блокируется публикация релизов любым маршрутом. Поэтому публикация релизов из
226
+ облака невозможна — облачный прогон готовит релиз и передаёт публикацию
227
+ человеку (см. `.agents/skills/release-auto/SKILL.md`). Диагностический
228
+ зонд права на диспатч: POST на
229
+ `actions/workflows/release.yml/dispatches` с несуществующим `ref` —
230
+ `422 No ref found` означает «право есть», `403` — «нет».
231
+
206
232
  ---
207
233
 
208
234
  ## PR-воркфлоу
@@ -251,9 +277,46 @@ stories/release-notes/v1/<текущая версия>.mdx
251
277
 
252
278
  Компоненты внутри секции — в алфавитном порядке.
253
279
 
254
- Не нужно добавлять release notes для:
255
- - изменений только в stories/тестах/документации
256
- - рефакторинга без изменения публичного API
280
+ ### Главное правило: пиши только то, что стало новым для потребителя
281
+
282
+ Release notes читает потребитель библиотеки, а не разработчик репозитория.
283
+ Запись отвечает на один вопрос: **что изменилось для того, кто подключает
284
+ компонент**. Всё остальное — лишнее.
285
+
286
+ **Не попадает в release notes:**
287
+
288
+ - внутренняя кухня репозитория: количество unit-тестов, созданные `*-ai.md`,
289
+ обновлённые stories и `ROADMAP.md`, JSDoc, переименования внутренних констант,
290
+ порядок импортов, чистка LESS;
291
+ - перечисление того, что **не** изменилось: списки прежних props, значений
292
+ enum'ов, barrel-экспортов, границ диапазонов. Если поведение не изменилось,
293
+ достаточно сказать это одной фразой — без инвентаризации;
294
+ - обоснования и подробности реализации, интересные только ревьюеру. Им место
295
+ в описании PR и в `{Component}-ai.md`.
296
+
297
+ **Рефакторинг без изменений для потребителя — одна короткая строка:**
298
+
299
+ ```mdx
300
+ **ComponentName**
301
+
302
+ - Проведён AI-рефакторинг.
303
+ ```
304
+
305
+ Если рефакторинг всё же дал потребителю что-то наблюдаемое (например
306
+ `displayName`, видимый в React DevTools) — назови только это:
307
+
308
+ ```mdx
309
+ **MediaWidth**
310
+
311
+ - Проведён AI-рефакторинг, добавлен `displayName`.
312
+ ```
313
+
314
+ Ориентир по объёму: запись про изменение поведения — 1–3 предложения, запись
315
+ про рефакторинг — одна строка. Если запись разрослась до абзаца, скорее всего
316
+ в неё попала внутренняя кухня — вычеркни её.
317
+
318
+ Не нужно добавлять release notes для изменений только в stories, тестах или
319
+ документации.
257
320
 
258
321
  ---
259
322
 
@@ -274,6 +274,32 @@ AI-агента.
274
274
 
275
275
  ---
276
276
 
277
+ ## Как заполнять `related` в AI.md
278
+
279
+ Индекс переходов для агента, а не «похожие компоненты»: только имена, пояснения —
280
+ в разделе «Связанные компоненты». Имя обязано быть адресатом: компонентом из
281
+ таблицы `docs/ai/ROADMAP.md`, у которого AI.md есть или запланирован. Связь с
282
+ обёрткой вне таблицы записывается на её родителя (`IDropdownMobileMaskedInputProps
283
+ extends IFormFieldMaskedInputProps` → `FormField`).
284
+
285
+ Адресат попадает в `related`, только если подходит под одну из четырёх категорий:
286
+
287
+ | # | Категория | Критерий | Направление |
288
+ |---|---|---|---|
289
+ | 1 | Семья | Родитель ↔ часть со своим AI.md | обе стороны |
290
+ | 2 | Альтернатива | Решает ту же задачу другим компромиссом — реально спутают (`Island` → `IslandAccordion`). Отвод в другой класс задач («нужен отступ — возьми `Gap`») остаётся прозой в «Не используй когда» | обе стороны |
291
+ | 3 | Контракт по рендеру | Импортирует и рендерит внутри себя (`Island` → `LoaderScreen`) | к зависимости |
292
+ | 4 | Контракт по типам | Наследует чужой интерфейс props, в т.ч. `Pick<>` / `Omit<>` (`IConfirmProps extends IIslandProps` → у `Confirm` пишем `Island`). Наследование от React и от внутренних базовых интерфейсов не считается | от наследника к базе |
293
+
294
+ Потребители не включаются — ссылка стоит с их стороны; исключение только для
295
+ потребителя, который опирается на хрупкую деталь.
296
+
297
+ Категории 1 и 2 симметричны, 3 и 4 односторонние. Порядок — семья → альтернативы
298
+ → контракты, ориентир 2–5 имён. Каждое имя из `related` должно быть объяснено в
299
+ разделе «Связанные компоненты»; обратное не требуется.
300
+
301
+ ---
302
+
277
303
  ## Внешние ресурсы
278
304
 
279
305
  - Документация для разработчиков и дизайнеров: https://triplex-design.ru/next/ru/Web/Components
@@ -180,7 +180,7 @@ Playground показывает только те props, которые реал
180
180
 
181
181
  ### Когда не создавать Playground
182
182
 
183
- Если у компонента нет настраиваемых props (только `children` и стандартные HTML-атрибуты), Playground не создаётся — нечего класть в Controls. Типичный случай: контейнер-обёртка вроде `ListItem`, `ListItemControls`, `ChipGroup`.
183
+ Если у компонента нет настраиваемых props (только `children` и стандартные HTML-атрибуты), Playground не создаётся — нечего класть в Controls. Типичный случай: контейнер-обёртка вроде `ListItem`, `ListItemControls`.
184
184
 
185
185
  В этом случае:
186
186
 
@@ -332,6 +332,7 @@ import { action } from "storybook/actions";
332
332
  **Исключаются при необходимости:**
333
333
 
334
334
  - Стори, дублирующие отображение другой стори. Если две стори рендерят визуально одно и то же — для теста оставьте одну, вторую исключите через `testRunner: { skip: true }`. Не нужно генерировать идентичные скриншоты.
335
+ - **Компоненты без визуального интерфейса** — `KeyDownListener`, `ComposedKeyDownListener` и подобные обёртки, которые не рендерят собственной разметки (слушатели событий, провайдеры контекста). На скриншот попадает только обвязка примеров, поэтому регрессию компонента такой тест поймать не может, а перегенерации требует от любой правки примера. Набор исключается **целиком**: `testRunner: { skip: true }` ставится в `parameters` у `meta`, отдельные стори параметр не дублируют, стори `VisualTests` не создаётся, файлов в `__screenshots__/` быть не должно. Поведение таких компонентов покрывается unit-тестами.
335
336
 
336
337
  ### Когда создавать Visual tests
337
338
 
@@ -664,6 +665,7 @@ export { default as SizesSource } from "./Sizes?raw";
664
665
 
665
666
  Скриншот-тесты обязательны для UI-компонентов. Без baseline-файлов в `__screenshots__/` регрессию не поймать.
666
667
 
668
+ - [ ] Компонент действительно рендерит собственную разметку. Если нет (слушатель событий, провайдер) — весь набор исключён через `testRunner: { skip: true }` в `meta`, `VisualTests` нет, baseline-файлов нет; остальные пункты этого раздела не применяются
667
669
  - [ ] Playground исключён из скриншот-тестов (`testRunner: { skip: true }`)
668
670
  - [ ] Нет дублирующих скриншотов — визуально идентичные стори исключены через `testRunner: { skip: true }`
669
671
  - [ ] Все ключевые визуальные состояния покрыты (размеры, статусы, заполненные поля, фокус)
@@ -18,7 +18,7 @@ version: "1.0"
18
18
 
19
19
  Frontmatter:
20
20
  - tokens: список CSS-переменных, которые компонент использует
21
- - related: список связанных компонентов (строки, имена)
21
+ - related: индекс переходов для агента. Полное правило: docs/ai/CONTEXT.md → «Как заполнять related в AI.md».
22
22
 
23
23
  Таблица Stories: колонка `Example file` обязательна — её читает сборщик
24
24
  mcp-data.json, чтобы инлайнить код примера. См. раздел Stories ниже.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "triplexVersion": "1.41.0",
4
- "generatedAt": "2026-08-07T11:02:25.745Z",
3
+ "triplexVersion": "1.43.0",
4
+ "generatedAt": "2026-08-21T12:41:11.790Z",
5
5
  "components": [
6
6
  {
7
7
  "name": "AbstractTree",
@@ -27,9 +27,15 @@
27
27
  "file": "components/AlertContext.md",
28
28
  "ai_ready": true
29
29
  },
30
+ {
31
+ "name": "AlertProcess",
32
+ "path": "src/components/Alert/AlertProcess-ai.md",
33
+ "file": "components/AlertProcess.md",
34
+ "ai_ready": true
35
+ },
30
36
  {
31
37
  "name": "Amount",
32
- "path": "src/components/Amount/Amount-AI.md",
38
+ "path": "src/components/Amount/Amount-ai.md",
33
39
  "file": "components/Amount.md",
34
40
  "ai_ready": true
35
41
  },
@@ -47,13 +53,13 @@
47
53
  },
48
54
  {
49
55
  "name": "Badge",
50
- "path": "src/components/Badge/Badge-AI.md",
56
+ "path": "src/components/Badge/Badge-ai.md",
51
57
  "file": "components/Badge.md",
52
58
  "ai_ready": true
53
59
  },
54
60
  {
55
61
  "name": "Body",
56
- "path": "src/components/Body/Body-AI.md",
62
+ "path": "src/components/Body/Body-ai.md",
57
63
  "file": "components/Body.md",
58
64
  "ai_ready": true
59
65
  },
@@ -114,7 +120,7 @@
114
120
  },
115
121
  {
116
122
  "name": "CarouselExtended",
117
- "path": "src/components/CarouselExtended/CarouselExtended-AI.md",
123
+ "path": "src/components/CarouselExtended/CarouselExtended-ai.md",
118
124
  "file": "components/CarouselExtended.md",
119
125
  "ai_ready": true
120
126
  },
@@ -132,7 +138,7 @@
132
138
  },
133
139
  {
134
140
  "name": "CheckboxTreeExtended",
135
- "path": "src/components/CheckboxTreeExtended/CheckboxTreeExtended-AI.md",
141
+ "path": "src/components/CheckboxTreeExtended/CheckboxTreeExtended-ai.md",
136
142
  "file": "components/CheckboxTreeExtended.md",
137
143
  "ai_ready": true
138
144
  },
@@ -156,7 +162,7 @@
156
162
  },
157
163
  {
158
164
  "name": "ChipGroup",
159
- "path": "src/components/ChipGroup/ChipGroup-AI.md",
165
+ "path": "src/components/ChipGroup/ChipGroup-ai.md",
160
166
  "file": "components/ChipGroup.md",
161
167
  "ai_ready": true
162
168
  },
@@ -184,6 +190,12 @@
184
190
  "file": "components/CollapsibleTreeExtended.md",
185
191
  "ai_ready": true
186
192
  },
193
+ {
194
+ "name": "ComposedKeyDownListener",
195
+ "path": "src/components/KeyDownListener/ComposedKeyDownListener-ai.md",
196
+ "file": "components/ComposedKeyDownListener.md",
197
+ "ai_ready": true
198
+ },
187
199
  {
188
200
  "name": "Confirm",
189
201
  "path": "src/components/Confirm/Confirm-AI.md",
@@ -449,6 +461,12 @@
449
461
  "file": "components/LoaderScreen.md",
450
462
  "ai_ready": true
451
463
  },
464
+ {
465
+ "name": "LoaderSmall",
466
+ "path": "src/components/Loader/LoaderSmall-ai.md",
467
+ "file": "components/LoaderSmall.md",
468
+ "ai_ready": true
469
+ },
452
470
  {
453
471
  "name": "Marker",
454
472
  "path": "src/components/Marker/Marker-AI.md",
@@ -461,15 +479,21 @@
461
479
  "file": "components/MarkerStatus.md",
462
480
  "ai_ready": true
463
481
  },
482
+ {
483
+ "name": "MaskedField",
484
+ "path": "src/components/TextField/MaskedField-ai.md",
485
+ "file": "components/MaskedField.md",
486
+ "ai_ready": true
487
+ },
464
488
  {
465
489
  "name": "MediaWidth",
466
- "path": "src/components/MediaWidth/MediaWidth-AI.md",
490
+ "path": "src/components/MediaWidth/MediaWidth-ai.md",
467
491
  "file": "components/MediaWidth.md",
468
492
  "ai_ready": true
469
493
  },
470
494
  {
471
495
  "name": "MobileView",
472
- "path": "src/components/MobileView/MobileView-AI.md",
496
+ "path": "src/components/MobileView/MobileView-ai.md",
473
497
  "file": "components/MobileView.md",
474
498
  "ai_ready": true
475
499
  },
@@ -560,7 +584,7 @@
560
584
  },
561
585
  {
562
586
  "name": "SelectExtendedField",
563
- "path": "src/components/SelectExtendedField/SelectExtendedField-AI.md",
587
+ "path": "src/components/SelectExtendedField/SelectExtendedField-ai.md",
564
588
  "file": "components/SelectExtendedField.md",
565
589
  "ai_ready": true
566
590
  },
@@ -584,7 +608,7 @@
584
608
  },
585
609
  {
586
610
  "name": "SliderExtended",
587
- "path": "src/components/SliderExtended/SliderExtended-AI.md",
611
+ "path": "src/components/SliderExtended/SliderExtended-ai.md",
588
612
  "file": "components/SliderExtended.md",
589
613
  "ai_ready": true
590
614
  },
@@ -662,7 +686,7 @@
662
686
  },
663
687
  {
664
688
  "name": "TabsExtended",
665
- "path": "src/components/TabsExtended/TabsExtended-AI.md",
689
+ "path": "src/components/TabsExtended/TabsExtended-ai.md",
666
690
  "file": "components/TabsExtended.md",
667
691
  "ai_ready": true
668
692
  },
@@ -753,10 +777,15 @@
753
777
  },
754
778
  {
755
779
  "name": "UnorderedList",
756
- "path": "src/components/UnorderedList",
757
- "file": null,
758
- "ai_ready": false,
759
- "storiesPath": "stories/UnorderedList.stories.tsx"
780
+ "path": "src/components/UnorderedList/UnorderedList-ai.md",
781
+ "file": "components/UnorderedList.md",
782
+ "ai_ready": true
783
+ },
784
+ {
785
+ "name": "UnorderedListExtended",
786
+ "path": "src/components/UnorderedListExtended/UnorderedListExtended-ai.md",
787
+ "file": "components/UnorderedListExtended.md",
788
+ "ai_ready": true
760
789
  },
761
790
  {
762
791
  "name": "UploadZone",
@@ -767,10 +796,9 @@
767
796
  },
768
797
  {
769
798
  "name": "WindowResizeListener",
770
- "path": "src/components/WindowResizeListener",
771
- "file": null,
772
- "ai_ready": false,
773
- "storiesPath": null
799
+ "path": "src/components/WindowResizeListener/WindowResizeListener-ai.md",
800
+ "file": "components/WindowResizeListener.md",
801
+ "ai_ready": true
774
802
  },
775
803
  {
776
804
  "name": "ListItemTable",
@@ -1013,11 +1041,26 @@
1013
1041
  "file": "release-notes/1.41.0.mdx",
1014
1042
  "path": "stories/release-notes/v1/1.41.0.mdx"
1015
1043
  },
1044
+ {
1045
+ "version": "1.41.1",
1046
+ "file": "release-notes/1.41.1.mdx",
1047
+ "path": "stories/release-notes/v1/1.41.1.mdx"
1048
+ },
1016
1049
  {
1017
1050
  "version": "1.42.0",
1018
1051
  "file": "release-notes/1.42.0.mdx",
1019
1052
  "path": "stories/release-notes/v1/1.42.0.mdx"
1020
1053
  },
1054
+ {
1055
+ "version": "1.43.0",
1056
+ "file": "release-notes/1.43.0.mdx",
1057
+ "path": "stories/release-notes/v1/1.43.0.mdx"
1058
+ },
1059
+ {
1060
+ "version": "1.44.0",
1061
+ "file": "release-notes/1.44.0.mdx",
1062
+ "path": "stories/release-notes/v1/1.44.0.mdx"
1063
+ },
1021
1064
  {
1022
1065
  "version": "1.5.0",
1023
1066
  "file": "release-notes/1.5.0.mdx",
@@ -0,0 +1,18 @@
1
+ import { Meta, Title, Heading } from "@storybook/addon-docs/blocks";
2
+
3
+ <Meta title="release-notes/v1/1.41.1" />
4
+
5
+ <Title>v1.41.1</Title>
6
+
7
+ <Heading>Изменения</Heading>
8
+
9
+ **Page**
10
+
11
+ - `Page.Header` и `Page.Footer` со свойством `sticky` теперь прилипают и вне `LightBox` при ширине viewport от 992px. При высоте viewport до 800px включительно sticky-позиционирование не применяется.
12
+
13
+ **SelectExtendedField**
14
+
15
+ - Исправлено: `onOpen` / `onClose` ложно срабатывали при повторном маунте в React 18 StrictMode — эффект опирался на флаг «смонтирован». Теперь сравнивается предыдущее значение состояния открытости, поэтому колбэки вызываются только на реальную смену состояния и по-прежнему не вызываются на первом рендере.
16
+ - Исправлено: клик по кнопке очистки (`onClear`) в `SelectExtendedFieldTarget` заодно раскрывал выпадающий блок — событие всплывало до обработчика клика по полю. Теперь всплытие останавливается, как это уже сделано в `Chip.Select` и `Chip.DatePicker`. Затрагивает также `SelectField` и `MultiselectField`, использующие тот же `Target`.
17
+ - Исправлено: `SelectExtendedFieldTarget` подмешивал в атрибут `class` литерал `undefined` — в `clsx` попадали ключи `styles.loading`, `styles.placeholder` и `styles.label`, которых нет в LESS-модуле компонента. Стили этих классов никогда не существовали, поэтому визуально ничего не меняется. Затрагивает также `SelectField` и `MultiselectField.Target`, использующие тот же компонент.
18
+ - AI-рефакторинг: JSDoc на всех props и рендер-функциях, константы-маппинги размера приведены к общему стилю именования, коды клавиш открытия вынесены в константу модуля, слушатель `mousedown` на документе навешивается только пока выпадающий блок открыт, callback-ref корневого элемента стабилизирован, в `SelectExtendedFieldTarget` убрана неиспользуемая внутренняя ссылка (ref по-прежнему указывает на элемент значения), в `SelectExtendedFieldDropdownDefault` дедуплицирован список опций десктопной и мобильной версий, у `SelectExtendedFieldDropdown` и `SelectExtendedFieldDropdownDefault` добавлен `displayName`. Unit-тесты разбиты по субкомпонентам и расширены с 42 до 69 кейсов, stories переведены на modern pattern, создан `SelectExtendedField-ai.md`. Публичный API не изменён (props, их типы, barrel-экспорты, `forwardRef` — как были).