@sberbusiness/triplex-next-mcp-server 1.42.0-beta → 1.44.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.
- package/bundle/components/AlertProcess.md +3 -2
- package/bundle/components/BodyPage.md +72 -4
- package/bundle/components/Button.md +1 -0
- package/bundle/components/Checkbox.md +7 -1
- package/bundle/components/CheckboxTreeExtended.md +149 -248
- package/bundle/components/CheckboxXGroup.md +2 -0
- package/bundle/components/CheckboxYGroup.md +7 -2
- package/bundle/components/ChipGroup.md +195 -126
- package/bundle/components/CollapsibleTreeExtended.md +6 -1
- package/bundle/components/DatePickerExtended.md +409 -262
- package/bundle/components/ExpandAnimation.md +263 -218
- package/bundle/components/Footer.md +235 -278
- package/bundle/components/Island.md +36 -1
- package/bundle/components/LightBox.md +14 -3
- package/bundle/components/MediaWidth.md +7 -1
- package/bundle/components/Notification.md +21 -3
- package/bundle/components/Overlay.md +24 -1
- package/bundle/components/Pagination.md +12 -13
- package/bundle/components/SliderExtended.md +358 -222
- package/bundle/components/TableBasic.md +2294 -0
- package/bundle/components/TabsExtended.md +380 -194
- package/bundle/components/Tag.md +259 -78
- package/bundle/components/TopOverlay.md +366 -90
- package/bundle/components/TreeView.md +343 -166
- package/bundle/components/TreeViewNode.md +436 -0
- package/bundle/components/UnorderedList.md +254 -0
- package/bundle/components/UnorderedListExtended.md +255 -0
- package/bundle/components/WindowResizeListener.md +249 -0
- package/bundle/guides/commits.md +121 -15
- package/bundle/guides/stories.md +1 -1
- package/bundle/manifest.json +46 -20
- package/bundle/release-notes/1.43.0.mdx +87 -0
- package/bundle/release-notes/1.44.0.mdx +120 -0
- package/bundle/release-notes/1.45.0.mdx +7 -0
- 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
|
+
|
package/bundle/guides/commits.md
CHANGED
|
@@ -56,11 +56,14 @@ TRI-21-alert-ai-ready
|
|
|
56
56
|
`docs/human/release.md`. Прямой push в `release-0` проверяется CI:
|
|
57
57
|
`ci.yml` триггерится на `push: branches: [release-0]`.
|
|
58
58
|
|
|
59
|
-
**Единственное
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
**Единственное исключение** — релизный коммит (бамп версии + release notes):
|
|
60
|
+
он тоже попадает в `release-0` напрямую, без PR. В еженедельном релизе его
|
|
61
|
+
делает [`release-weekly.yml`](../../.github/workflows/release-weekly.yml),
|
|
62
|
+
при ручном выпуске — skill `release-react17`. Прогон CI на этот коммит
|
|
63
|
+
стартует, но дождаться его нельзя: релиз создаётся сразу после push. Поэтому
|
|
64
|
+
исключение компенсируется гейтами перед пушем — `npm run test-unit`
|
|
65
|
+
и `npm run build` (последний в `prebuild` гоняет `tsc -p tsconfig.build.json`;
|
|
66
|
+
полный `tsc` гейтом быть не может — он даёт давние ошибки в `stories/`).
|
|
64
67
|
|
|
65
68
|
---
|
|
66
69
|
|
|
@@ -86,6 +89,24 @@ TRI-21 Alert: AI refactoring + unit-тесты
|
|
|
86
89
|
- Тело коммита опционально (для сложных изменений).
|
|
87
90
|
- Язык: русский или английский — не смешивать в одном коммите.
|
|
88
91
|
|
|
92
|
+
**Исключение — релизные коммиты еженедельного релиза.** Их делает
|
|
93
|
+
[`release-weekly.yml`](../../.github/workflows/release-weekly.yml) в GitHub
|
|
94
|
+
Actions, и они идут **без префикса задачи**:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Подготовка релиза 0.44.0
|
|
98
|
+
Подготовка релиза 1.44.0
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Причина техническая: у Actions нет доступа к Linear, номер задачи взять
|
|
102
|
+
неоткуда, а выдумывать его нельзя. Связь с трекером даёт skill `release-auto`
|
|
103
|
+
— он заводит задачу «Релиз 1.Y.0 / 0.Y.0» по итогам прогона и складывает
|
|
104
|
+
туда ссылки на релизы. Коммит машинный, его авторство видно по автору
|
|
105
|
+
(`dudim-ai`), поэтому потеря префикса не мешает разбору истории.
|
|
106
|
+
|
|
107
|
+
При **ручном** выпуске (`/release`) префикс обязателен, как обычно: там
|
|
108
|
+
задача Linear заводится до начала работы.
|
|
109
|
+
|
|
89
110
|
---
|
|
90
111
|
|
|
91
112
|
## Когда запускать линтер
|
|
@@ -140,7 +161,8 @@ npx prettier --write src/components/Button/Button.tsx
|
|
|
140
161
|
|
|
141
162
|
1. делает коммит по конвенциям этого файла (skill `commit-component`);
|
|
142
163
|
2. пушит ветку;
|
|
143
|
-
3.
|
|
164
|
+
3. дожидается PR от workflow `auto-pr.yml` (см. «PR-воркфлоу»; сам
|
|
165
|
+
`gh pr create` в ветках `TRI-*` не вызывает);
|
|
144
166
|
4. если менялись stories или визуал компонентов — обновляет baseline-скриншоты
|
|
145
167
|
через skill `update-visual-baselines` (CI-workflow «Update Visual
|
|
146
168
|
Snapshots» + чистка orphan-скриншотов);
|
|
@@ -161,9 +183,9 @@ npx prettier --write src/components/Button/Button.tsx
|
|
|
161
183
|
|
|
162
184
|
## GitHub-операции от аккаунта бота
|
|
163
185
|
|
|
164
|
-
Автоматические GitHub-операции (
|
|
165
|
-
|
|
166
|
-
|
|
186
|
+
Автоматические GitHub-операции (комментарии в PR, запуск workflow,
|
|
187
|
+
авторство коммитов автофинала) выполняются от отдельного machine-аккаунта,
|
|
188
|
+
если у разработчика задан его токен:
|
|
167
189
|
|
|
168
190
|
```bash
|
|
169
191
|
# fine-grained PAT бота: contents:write, pull-requests:write, actions:write
|
|
@@ -171,11 +193,18 @@ export TRIPLEX_BOT_GH_TOKEN=... # в ~/.zshenv (не ~/.zshrc — агент
|
|
|
171
193
|
# в неинтерактивном shell), НЕ в репозитории
|
|
172
194
|
```
|
|
173
195
|
|
|
196
|
+
Тот же PAT хранится как repository secret `DUDIM_AI_GH_TOKEN` — его
|
|
197
|
+
использует workflow `auto-pr.yml`, который создаёт PR для веток `TRI-*`
|
|
198
|
+
(см. «PR-воркфлоу»). Благодаря этому автор PR один и тот же независимо от
|
|
199
|
+
того, где шла сессия агента — локально или в облаке. При ротации PAT
|
|
200
|
+
обнови оба места: `~/.zshenv` и secret
|
|
201
|
+
(`gh secret set DUDIM_AI_GH_TOKEN --body "$TRIPLEX_BOT_GH_TOKEN"`).
|
|
202
|
+
|
|
174
203
|
**Паттерн для агентов** — в начале Bash-вызова с командами `gh`:
|
|
175
204
|
|
|
176
205
|
```bash
|
|
177
206
|
if [ -n "$TRIPLEX_BOT_GH_TOKEN" ]; then export GH_TOKEN="$TRIPLEX_BOT_GH_TOKEN"; fi
|
|
178
|
-
gh pr
|
|
207
|
+
gh pr comment ... # выполнится от бота; без токена — от разработчика (fallback)
|
|
179
208
|
```
|
|
180
209
|
|
|
181
210
|
**Авторство коммитов автофинала** (identity бота выводится из токена,
|
|
@@ -203,20 +232,97 @@ fi
|
|
|
203
232
|
- Действия в Linear выполняются от сессии разработчика — бот на них
|
|
204
233
|
не распространяется.
|
|
205
234
|
|
|
235
|
+
### Облачные сессии: подмена токена прокси
|
|
236
|
+
|
|
237
|
+
В облачных сессиях (routines на claude.ai) запросы к `api.github.com`
|
|
238
|
+
идут **не** с `TRIPLEX_BOT_GH_TOKEN`: egress-прокси подменяет авторизацию
|
|
239
|
+
на токен GitHub App `claude` (user-to-server, identity — аккаунт
|
|
240
|
+
разработчика). Паттерн выше при этом не ломается — просто действующие
|
|
241
|
+
права определяет прокси, а не PAT. Как распознать:
|
|
242
|
+
|
|
243
|
+
- `gh api user` возвращает аккаунт разработчика, хотя в env лежит токен
|
|
244
|
+
бота;
|
|
245
|
+
- текст 403 — «Resource not accessible by **integration**» или «not
|
|
246
|
+
permitted for this session type / through this proxy» (у fine-grained
|
|
247
|
+
PAT было бы «…by personal access token»).
|
|
248
|
+
|
|
249
|
+
Что доступно из облака — проверено зондами TRI-117 (2026-08-14) и TRI-119
|
|
250
|
+
(2026-08-21):
|
|
251
|
+
|
|
252
|
+
| Операция | Из облака |
|
|
253
|
+
|---|---|
|
|
254
|
+
| `git push` ветки, включая защищённый `main` | ✅ |
|
|
255
|
+
| Создание и мерж PR, комментарии | ✅ |
|
|
256
|
+
| Чтение REST и Actions | ✅ |
|
|
257
|
+
| Диспатч нерелизных workflow (`visual-update.yml`) | ✅ |
|
|
258
|
+
| `git push` тега | ❌ `RPC failed; HTTP 403` |
|
|
259
|
+
| `POST /git/refs`, `PATCH /git/refs/heads/*` | ❌ |
|
|
260
|
+
| `gh release create` | ❌ |
|
|
261
|
+
| `gh workflow run release.yml`, `repository_dispatch` | ❌ |
|
|
262
|
+
| Удаление веток и тегов | ❌ |
|
|
263
|
+
|
|
264
|
+
Отказы приходят с текстом `Write access to this GitHub API path is not
|
|
265
|
+
permitted through this proxy` — это ограничение прокси, а не прав токена
|
|
266
|
+
и не настроек репозитория. Менять branch protection бесполезно.
|
|
267
|
+
|
|
268
|
+
**Следствие для релиза:** выпустить его из облачной сессии нельзя ни одним
|
|
269
|
+
маршрутом. Поэтому механика релиза живёт в GitHub Actions
|
|
270
|
+
(`.github/workflows/release-weekly.yml`), расписание держит сам GitHub, а
|
|
271
|
+
за облачной routine остаётся отчёт — см.
|
|
272
|
+
`.agents/skills/release-auto/SKILL.md`.
|
|
273
|
+
|
|
206
274
|
---
|
|
207
275
|
|
|
208
276
|
## PR-воркфлоу
|
|
209
277
|
|
|
210
|
-
**Работа по задаче Linear:**
|
|
278
|
+
**Работа по задаче Linear:** PR для веток `TRI-*` создаёт workflow
|
|
279
|
+
`auto-pr.yml` (триггер `on: push`) от аккаунта бота — secret
|
|
280
|
+
`DUDIM_AI_GH_TOKEN`. Так автор PR не зависит от среды сессии: локальный
|
|
281
|
+
`gh pr create` шёл бы от PAT бота, а облачный — от GitHub App `claude`
|
|
282
|
+
(прокси подменяет авторизацию, см. «Облачные сессии»). Агент **не вызывает
|
|
283
|
+
`gh pr create` в ветках `TRI-*`** — иначе облачная сессия успеет создать
|
|
284
|
+
PR от `claude[bot]` раньше workflow.
|
|
285
|
+
|
|
286
|
+
Финальный коммит — это и есть источник PR: заголовок берётся из первой
|
|
287
|
+
строки (`TRI-XXX Краткое описание`), описание — из тела коммита. Поэтому
|
|
288
|
+
в автофинале тело коммита обязательно и содержит описание PR
|
|
289
|
+
(«Что сделано» / «Как проверить»).
|
|
211
290
|
|
|
212
291
|
```bash
|
|
213
292
|
git push -u origin TRI-XXX-feature-name
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
--
|
|
293
|
+
# PR создаст auto-pr.yml через ~20–60 с; дождись его появления:
|
|
294
|
+
for i in $(seq 1 12); do
|
|
295
|
+
pr_url=$(gh pr list --head "TRI-XXX-feature-name" --base main --state open \
|
|
296
|
+
--json url --jq '.[0].url // empty') && [ -n "$pr_url" ] && break
|
|
297
|
+
sleep 10
|
|
298
|
+
done
|
|
299
|
+
echo "${pr_url:-PR не появился}"
|
|
217
300
|
```
|
|
218
301
|
|
|
219
|
-
|
|
302
|
+
Если PR не появился за ~2 минуты — проверь прогон workflow именно для
|
|
303
|
+
текущего коммита (не по ветке: `--limit 1` по ветке может показать прогон
|
|
304
|
+
предыдущего пуша):
|
|
305
|
+
`gh run list --workflow=auto-pr.yml --commit $(git rev-parse HEAD) --limit 1`.
|
|
306
|
+
Дальше по статусу:
|
|
307
|
+
|
|
308
|
+
- `queued` / `in_progress` — прогон ещё идёт, продолжай ждать;
|
|
309
|
+
- `completed` + `success` без PR — сработал guard (маркер `[no-pr]`,
|
|
310
|
+
subject не по формату `TRI-XXX ...` или ветка без новых коммитов
|
|
311
|
+
относительно `main`) — штатное завершение без PR, fallback не нужен;
|
|
312
|
+
- `completed` + `failure` или прогона нет (например, протух secret) —
|
|
313
|
+
fallback.
|
|
314
|
+
Fallback — прежняя схема: `gh pr create` с паттерном bot-токена из
|
|
315
|
+
раздела выше.
|
|
316
|
+
Fallback безопасен: раз workflow PR не создал, гонки нет. В облачной
|
|
317
|
+
сессии fallback-PR будет от `claude[bot]` (прокси подменит PAT — см.
|
|
318
|
+
«Облачные сессии») — это осознанная деградация, лучше PR от app'а, чем
|
|
319
|
+
отсутствие PR.
|
|
320
|
+
|
|
321
|
+
Обход автосоздания (WIP-пуш ветки `TRI-*` без PR): добавь маркер `[no-pr]`
|
|
322
|
+
в первую строку HEAD-коммита. Коммиты не по формату `TRI-XXX ...`
|
|
323
|
+
(например, `chore: ...`) PR также не создают.
|
|
324
|
+
|
|
325
|
+
После появления PR финал продолжается автоматически: skill
|
|
220
326
|
`update-visual-baselines` (если менялись stories/визуал) и skill
|
|
221
327
|
`finish-task` — комментарий-резюме в задаче Linear и проверка линковки PR
|
|
222
328
|
(GitHub-интеграция Linear подхватит PR по `TRI-XXX` в имени ветки).
|
package/bundle/guides/stories.md
CHANGED
|
@@ -180,7 +180,7 @@ Playground показывает только те props, которые реал
|
|
|
180
180
|
|
|
181
181
|
### Когда не создавать Playground
|
|
182
182
|
|
|
183
|
-
Если у компонента нет настраиваемых props (только `children` и стандартные HTML-атрибуты), Playground не создаётся — нечего класть в Controls. Типичный случай: контейнер-обёртка вроде `ListItem`, `ListItemControls
|
|
183
|
+
Если у компонента нет настраиваемых props (только `children` и стандартные HTML-атрибуты), Playground не создаётся — нечего класть в Controls. Типичный случай: контейнер-обёртка вроде `ListItem`, `ListItemControls`.
|
|
184
184
|
|
|
185
185
|
В этом случае:
|
|
186
186
|
|
package/bundle/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"triplexVersion": "1.
|
|
4
|
-
"generatedAt": "2026-08-
|
|
3
|
+
"triplexVersion": "1.44.0",
|
|
4
|
+
"generatedAt": "2026-08-28T11:32:35.105Z",
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
7
7
|
"name": "AbstractTree",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"name": "CheckboxTreeExtended",
|
|
141
|
-
"path": "src/components/CheckboxTreeExtended/CheckboxTreeExtended-
|
|
141
|
+
"path": "src/components/CheckboxTreeExtended/CheckboxTreeExtended-ai.md",
|
|
142
142
|
"file": "components/CheckboxTreeExtended.md",
|
|
143
143
|
"ai_ready": true
|
|
144
144
|
},
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
"name": "ChipGroup",
|
|
165
|
-
"path": "src/components/ChipGroup/ChipGroup-
|
|
165
|
+
"path": "src/components/ChipGroup/ChipGroup-ai.md",
|
|
166
166
|
"file": "components/ChipGroup.md",
|
|
167
167
|
"ai_ready": true
|
|
168
168
|
},
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
},
|
|
211
211
|
{
|
|
212
212
|
"name": "DatePickerExtended",
|
|
213
|
-
"path": "src/components/DatePickerExtended/DatePickerExtended-
|
|
213
|
+
"path": "src/components/DatePickerExtended/DatePickerExtended-ai.md",
|
|
214
214
|
"file": "components/DatePickerExtended.md",
|
|
215
215
|
"ai_ready": true
|
|
216
216
|
},
|
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
},
|
|
259
259
|
{
|
|
260
260
|
"name": "ExpandAnimation",
|
|
261
|
-
"path": "src/components/ExpandAnimation/ExpandAnimation-
|
|
261
|
+
"path": "src/components/ExpandAnimation/ExpandAnimation-ai.md",
|
|
262
262
|
"file": "components/ExpandAnimation.md",
|
|
263
263
|
"ai_ready": true
|
|
264
264
|
},
|
|
@@ -271,7 +271,7 @@
|
|
|
271
271
|
},
|
|
272
272
|
{
|
|
273
273
|
"name": "Footer",
|
|
274
|
-
"path": "src/components/Footer/Footer-
|
|
274
|
+
"path": "src/components/Footer/Footer-ai.md",
|
|
275
275
|
"file": "components/Footer.md",
|
|
276
276
|
"ai_ready": true
|
|
277
277
|
},
|
|
@@ -608,7 +608,7 @@
|
|
|
608
608
|
},
|
|
609
609
|
{
|
|
610
610
|
"name": "SliderExtended",
|
|
611
|
-
"path": "src/components/SliderExtended/SliderExtended-
|
|
611
|
+
"path": "src/components/SliderExtended/SliderExtended-ai.md",
|
|
612
612
|
"file": "components/SliderExtended.md",
|
|
613
613
|
"ai_ready": true
|
|
614
614
|
},
|
|
@@ -678,6 +678,12 @@
|
|
|
678
678
|
"file": "components/Table.md",
|
|
679
679
|
"ai_ready": true
|
|
680
680
|
},
|
|
681
|
+
{
|
|
682
|
+
"name": "TableBasic",
|
|
683
|
+
"path": "src/components/Table/TableBasic-ai.md",
|
|
684
|
+
"file": "components/TableBasic.md",
|
|
685
|
+
"ai_ready": true
|
|
686
|
+
},
|
|
681
687
|
{
|
|
682
688
|
"name": "Tabs",
|
|
683
689
|
"path": "src/components/Tabs/Tabs-AI.md",
|
|
@@ -686,7 +692,7 @@
|
|
|
686
692
|
},
|
|
687
693
|
{
|
|
688
694
|
"name": "TabsExtended",
|
|
689
|
-
"path": "src/components/TabsExtended/TabsExtended-
|
|
695
|
+
"path": "src/components/TabsExtended/TabsExtended-ai.md",
|
|
690
696
|
"file": "components/TabsExtended.md",
|
|
691
697
|
"ai_ready": true
|
|
692
698
|
},
|
|
@@ -698,7 +704,7 @@
|
|
|
698
704
|
},
|
|
699
705
|
{
|
|
700
706
|
"name": "Tag",
|
|
701
|
-
"path": "src/components/Tag/Tag-
|
|
707
|
+
"path": "src/components/Tag/Tag-ai.md",
|
|
702
708
|
"file": "components/Tag.md",
|
|
703
709
|
"ai_ready": true
|
|
704
710
|
},
|
|
@@ -752,16 +758,22 @@
|
|
|
752
758
|
},
|
|
753
759
|
{
|
|
754
760
|
"name": "TopOverlay",
|
|
755
|
-
"path": "src/components/TopOverlay/TopOverlay-
|
|
761
|
+
"path": "src/components/TopOverlay/TopOverlay-ai.md",
|
|
756
762
|
"file": "components/TopOverlay.md",
|
|
757
763
|
"ai_ready": true
|
|
758
764
|
},
|
|
759
765
|
{
|
|
760
766
|
"name": "TreeView",
|
|
761
|
-
"path": "src/components/TreeView/TreeView-
|
|
767
|
+
"path": "src/components/TreeView/TreeView-ai.md",
|
|
762
768
|
"file": "components/TreeView.md",
|
|
763
769
|
"ai_ready": true
|
|
764
770
|
},
|
|
771
|
+
{
|
|
772
|
+
"name": "TreeViewNode",
|
|
773
|
+
"path": "src/components/TreeView/TreeViewNode-ai.md",
|
|
774
|
+
"file": "components/TreeViewNode.md",
|
|
775
|
+
"ai_ready": true
|
|
776
|
+
},
|
|
765
777
|
{
|
|
766
778
|
"name": "Triggers",
|
|
767
779
|
"path": "src/components/Triggers/Triggers-AI.md",
|
|
@@ -777,10 +789,15 @@
|
|
|
777
789
|
},
|
|
778
790
|
{
|
|
779
791
|
"name": "UnorderedList",
|
|
780
|
-
"path": "src/components/UnorderedList",
|
|
781
|
-
"file":
|
|
782
|
-
"ai_ready":
|
|
783
|
-
|
|
792
|
+
"path": "src/components/UnorderedList/UnorderedList-ai.md",
|
|
793
|
+
"file": "components/UnorderedList.md",
|
|
794
|
+
"ai_ready": true
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
"name": "UnorderedListExtended",
|
|
798
|
+
"path": "src/components/UnorderedListExtended/UnorderedListExtended-ai.md",
|
|
799
|
+
"file": "components/UnorderedListExtended.md",
|
|
800
|
+
"ai_ready": true
|
|
784
801
|
},
|
|
785
802
|
{
|
|
786
803
|
"name": "UploadZone",
|
|
@@ -791,10 +808,9 @@
|
|
|
791
808
|
},
|
|
792
809
|
{
|
|
793
810
|
"name": "WindowResizeListener",
|
|
794
|
-
"path": "src/components/WindowResizeListener",
|
|
795
|
-
"file":
|
|
796
|
-
"ai_ready":
|
|
797
|
-
"storiesPath": null
|
|
811
|
+
"path": "src/components/WindowResizeListener/WindowResizeListener-ai.md",
|
|
812
|
+
"file": "components/WindowResizeListener.md",
|
|
813
|
+
"ai_ready": true
|
|
798
814
|
},
|
|
799
815
|
{
|
|
800
816
|
"name": "ListItemTable",
|
|
@@ -1052,6 +1068,16 @@
|
|
|
1052
1068
|
"file": "release-notes/1.43.0.mdx",
|
|
1053
1069
|
"path": "stories/release-notes/v1/1.43.0.mdx"
|
|
1054
1070
|
},
|
|
1071
|
+
{
|
|
1072
|
+
"version": "1.44.0",
|
|
1073
|
+
"file": "release-notes/1.44.0.mdx",
|
|
1074
|
+
"path": "stories/release-notes/v1/1.44.0.mdx"
|
|
1075
|
+
},
|
|
1076
|
+
{
|
|
1077
|
+
"version": "1.45.0",
|
|
1078
|
+
"file": "release-notes/1.45.0.mdx",
|
|
1079
|
+
"path": "stories/release-notes/v1/1.45.0.mdx"
|
|
1080
|
+
},
|
|
1055
1081
|
{
|
|
1056
1082
|
"version": "1.5.0",
|
|
1057
1083
|
"file": "release-notes/1.5.0.mdx",
|