wenay-react2 1.0.40 → 1.0.42

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 (75) hide show
  1. package/doc/EXAMPLE_USAGE.md +971 -0
  2. package/doc/PROJECT_FUNCTIONALITY.md +405 -0
  3. package/doc/PROJECT_RULES.md +80 -0
  4. package/doc/WENAY_REACT2_RENAMES.md +177 -0
  5. package/doc/changes/v1.0.35.md +26 -0
  6. package/doc/changes/v1.0.37.md +13 -0
  7. package/doc/changes/v1.0.38.md +48 -0
  8. package/doc/changes/v1.0.39.md +35 -0
  9. package/doc/changes/v1.0.40.md +15 -0
  10. package/doc/changes/v1.0.41.md +36 -0
  11. package/doc/changes/v1.0.42.md +26 -0
  12. package/doc/progress/README.md +14 -0
  13. package/doc/progress/architecture-fix-queue.md +74 -0
  14. package/doc/progress/hook-controller-opportunities.md +367 -0
  15. package/doc/progress/hook-extraction-audit.md +195 -0
  16. package/doc/progress/public-surface-normalization.md +368 -0
  17. package/doc/progress/style-system-normalization.md +121 -0
  18. package/doc/target/README.md +33 -0
  19. package/doc/target/my.md +81 -0
  20. package/doc/wenay-react2-1.0.8.tgz +0 -0
  21. package/doc/wenay-react2-rare.md +807 -0
  22. package/doc/wenay-react2.md +539 -0
  23. package/lib/common/api.d.ts +1 -0
  24. package/lib/common/api.js +8 -4
  25. package/lib/common/src/components/Dnd/DragArea.d.ts +5 -0
  26. package/lib/common/src/components/Dnd/DragArea.js +5 -1
  27. package/lib/common/src/components/Dnd/FloatingWindow.d.ts +9 -5
  28. package/lib/common/src/components/Dnd/FloatingWindow.js +39 -97
  29. package/lib/common/src/components/Dnd/Resizable.d.ts +3 -7
  30. package/lib/common/src/components/Dnd/Resizable.js +4 -3
  31. package/lib/common/src/components/Menu/RightMenu.js +8 -4
  32. package/lib/common/src/components/Menu/RightMenuStore.d.ts +2 -2
  33. package/lib/common/src/components/Menu/RightMenuStore.js +5 -3
  34. package/lib/common/src/components/Modal/LeftModal.js +4 -2
  35. package/lib/common/src/components/Modal/ModalContextProvider.js +5 -16
  36. package/lib/common/src/components/MyResizeObserver.d.ts +24 -0
  37. package/lib/common/src/components/MyResizeObserver.js +49 -0
  38. package/lib/common/src/components/Overlay.d.ts +24 -0
  39. package/lib/common/src/components/Overlay.js +28 -0
  40. package/lib/common/src/components/Settings/SettingsDialog.js +21 -22
  41. package/lib/common/src/components/Toolbar/Toolbar.d.ts +1 -0
  42. package/lib/common/src/components/Toolbar/Toolbar.js +16 -23
  43. package/lib/common/src/grid/columnState/ColumnsMenu.js +4 -13
  44. package/lib/common/src/grid/columnState/columnGrid.d.ts +103 -0
  45. package/lib/common/src/grid/columnState/columnGrid.js +231 -0
  46. package/lib/common/src/grid/columnState/columnState.js +10 -9
  47. package/lib/common/src/grid/columnState/index.js +3 -0
  48. package/lib/common/src/hooks/useDraggable.d.ts +8 -0
  49. package/lib/common/src/hooks/useDraggable.js +13 -4
  50. package/lib/common/src/hooks/useOutside.d.ts +4 -1
  51. package/lib/common/src/hooks/useOutside.js +2 -1
  52. package/lib/common/src/logs/logs.d.ts +123 -5
  53. package/lib/common/src/logs/logs.js +218 -130
  54. package/lib/common/src/logs/logsController.d.ts +3 -0
  55. package/lib/common/src/styles/tokens.d.ts +7 -6
  56. package/lib/common/src/styles/tokens.js +8 -5
  57. package/lib/common/src/utils/cache.d.ts +12 -0
  58. package/lib/common/src/utils/cache.js +0 -0
  59. package/lib/common/src/utils/fixedOrder.d.ts +15 -0
  60. package/lib/common/src/utils/fixedOrder.js +30 -0
  61. package/lib/common/src/utils/index.d.ts +2 -0
  62. package/lib/common/src/utils/index.js +2 -0
  63. package/lib/common/src/utils/memoryStore.d.ts +3 -6
  64. package/lib/common/src/utils/memoryStore.js +1 -3
  65. package/lib/common/src/utils/persistedMaps.d.ts +16 -0
  66. package/lib/common/src/utils/persistedMaps.js +5 -0
  67. package/lib/common/src/utils/searchHistory.js +7 -6
  68. package/lib/common/src/utils/structEqual.d.ts +12 -0
  69. package/lib/common/src/utils/structEqual.js +40 -0
  70. package/lib/common/updateBy.d.ts +3 -0
  71. package/lib/common/updateBy.js +57 -22
  72. package/lib/style/menuRight.css +29 -23
  73. package/lib/style/style.css +73 -10
  74. package/lib/style/tokens.css +8 -4
  75. package/package.json +3 -2
@@ -0,0 +1,367 @@
1
+ # Hook/controller opportunities audit
2
+
3
+ Дата: 2026-07-09.
4
+
5
+ Задача: пройтись по коду библиотеки и записать места, где есть смысл продолжать переводить функционал в стиль `use*` hook / `create*` controller + тонкий visual wrapper. Не записывать задачи ради задач: у каждой позиции есть оценка эффективности, простоты и риска.
6
+
7
+ ## Проверенные зоны
8
+
9
+ - `src/common/src/logs/miniLogs.tsx`
10
+ - `src/common/src/logs/logs.tsx`
11
+ - `src/common/src/logs/logsContext.tsx`
12
+ - `src/common/src/components/ParamsEditor.tsx`
13
+ - `src/common/src/components/Other.tsx`
14
+ - `src/common/src/components/Settings/SettingsDialog.tsx`
15
+ - `src/common/src/menu/menu.tsx`
16
+ - `src/common/src/menu/menuMouse.tsx`
17
+ - `src/common/src/components/Menu/RightMenu.tsx`
18
+ - `src/common/src/components/Menu/StickerMenu.tsx`
19
+ - `src/common/src/components/Dnd/FloatingWindow.tsx`
20
+ - `src/common/src/components/Dnd/Resizable.tsx`
21
+ - `src/common/src/hooks/useDraggable.tsx`
22
+ - `src/common/src/components/MyResizeObserver.tsx`
23
+ - QA usage references in `src/common/testUseReact/qa.tsx`
24
+
25
+ ## Execution updates
26
+
27
+ ### MiniLogs layered redesign — done
28
+
29
+ Сделано:
30
+ - добавлены `useMiniLogsTable`, `MiniLogsView`, `MiniLogsTable` и compatibility wrapper `MiniLogs`;
31
+ - hook возвращает table props, `gridProps` alias, `apiRef`, `fit`, `getApi`, `withApi`, `onGridReady`, `onGridPreDestroyed`, `onCellMouseDown`;
32
+ - сохранён старый `rowData` path, чтобы не менять row identity для повторяющихся log `id`;
33
+ - MiniLogs-owned store/append/history не добавлялись: реального consumer для этого пока нет;
34
+ - обновлены `doc/EXAMPLE_USAGE.md`, `doc/wenay-react2.md`, `doc/wenay-react2-rare.md`, QA card 9 и добавлен `__test/miniLogs.test.tsx`.
35
+
36
+ Проверено:
37
+ - `npm run testjest -- --runInBand miniLogs.test.tsx`;
38
+ - `npx tsc -p tsconfig.qa-check.json --noEmit`;
39
+ - `npm run testjest -- --runInBand`;
40
+ - `npm run build`;
41
+ - `git diff --check`.
42
+
43
+ ## Очередь полезных задач
44
+
45
+ ### 1. MiniLogs layered redesign
46
+
47
+ Статус: выполнено; оставить как reference implementation для следующих hook/controller passes.
48
+
49
+ Что сделать:
50
+ - слой 1: `useMiniLogsTable(...)` или `createMiniLogsTableController(...)`, который готовит `columns`, `defaultColDef`, callbacks и grid contract;
51
+ - слой 2: `MiniLogsTable` / `MiniLogsView` как тонкий visual wrapper на `AgGridTable`;
52
+ - слой 3: совместимый `<MiniLogs data onClick />`.
53
+
54
+ Оценка:
55
+ - эффективность: medium/high;
56
+ - простота: medium;
57
+ - риск: medium.
58
+
59
+ Почему задача не высосана из пальца:
60
+ - текущий `MiniLogs` уже использует наши grid helpers, но сам API остаётся плоским компонентом;
61
+ - это небольшой публичный компонент, его можно проверить отдельно без переписывания всего logger API.
62
+
63
+ Проверка:
64
+ - найти или добавить QA usage для `MiniLogs`;
65
+ - проверить отображение времени, click callback, fit/resize таблицы.
66
+
67
+ ### 2. ParamsEditor / ParamsEdit controller split
68
+
69
+ Статус: first controller layer done; full renderer/view split deferred.
70
+
71
+ Что сделано:
72
+ - добавлен `useParamsEditorController(...)` в `ParamsEditor.tsx`;
73
+ - controller владеет mutable draft clone, immediate notify, delayed notify, expand callback и cleanup таймера;
74
+ - `ParamsEditorBase` использует controller, но сложная JSX-разметка rows/arrays/inputs не переписывалась;
75
+ - `ParamsEditor`, `ParamsEdit`, `ParamsArrayEdit` остались compatibility wrappers.
76
+
77
+ Что не сделано сейчас:
78
+ - `ParamsEditorView` / row-model split: слишком большой pass, высокий риск array mutation/resize/autostep UX;
79
+ - async load/save hook для `ParamsEdit`/`ParamsArrayEdit`: полезно только отдельным pass с policy tests;
80
+ - стили/плотность/buttons add/remove не трогались.
81
+
82
+ Оценка:
83
+ - эффективность: medium/high;
84
+ - простота: medium;
85
+ - риск: medium, потому что renderer остался на месте.
86
+
87
+ Проверка:
88
+ - `paramsEditorController.test.tsx`: draft clone isolation, delayed notify + unmount cleanup, current `onExpand` callback;
89
+ - QA card 7/10 остаются ручными regression points для обычного renderer/save behavior.
90
+
91
+ ### 3. Logs API controller split
92
+
93
+ Статус: headless controller pass done; full UI split deferred.
94
+
95
+ Что видно сейчас:
96
+ - `logs.tsx` хранит global maps (`cashLogs`, `datumConst`, `datumMiniConst`), settings, rendering API, table UI and notification UI вместе;
97
+ - `logsContext.tsx` уже предлагает отдельный context-based logger, но это параллельный surface;
98
+ - `PageLogs` и `MiniLogs` уже переведены на `AgGridTable`; `LogsTable` из `logsContext.tsx` теперь тоже переведён на `AgGridTable` + `colDefCentered` без изменения context state;
99
+ - right-click copy action in `PageLogs` now has explicit `actionKey: "logs.copyCell"` for contextMenu action stats.
100
+
101
+ Что сделано:
102
+ - добавлен `src/common/src/logs/logsController.ts`;
103
+ - экспортированы `LogsApiOptions`, `LogsController`, `LogsControllerState`, `createLogsControllerState`, `createLogsController` через `logs.tsx`;
104
+ - `LogInput` / `LogEntry` остались в `logsController.ts`, но не re-export через общий `api.tsx`, потому эти имена уже заняты `logsContext.tsx`;
105
+ - вынесен headless state/API слой: `addLogs`, `limit/limitPer`, `num`, `getRows()`, `getMiniRows()`, `getLatest()`, `params.def/get/set`, update emitters;
106
+ - `getLogsApi`, `logsApi`, `PageLogs`, `MessageEventLogs`, `LogsPage`, `logsApi.React.*` оставлены compatibility wrappers;
107
+ - `logsContext.tsx` не объединялся с global logger.
108
+
109
+ Оценка:
110
+ - эффективность: high для headless controller;
111
+ - простота: medium;
112
+ - риск: medium, если UI не переписывать.
113
+
114
+ Дополнительный context logger pass:
115
+ - добавлены `useLogsTableController()` и `useLogsNotificationsController()`;
116
+ - `LogsTable` и `LogsNotifications` остались compatibility visual wrappers;
117
+ - provider state, localStorage settings, notification timers and visual styles не менялись.
118
+
119
+ Что пропустить:
120
+ - публичный конфликт имён `logsContext.LogInput/LogEntry` решать отдельно, если понадобится единый logger contract;
121
+ - merge/deprecate `logsContext.tsx` без отдельного решения;
122
+ - MiniLogs-owned store/history без реального consumer.
123
+ ### 4. SettingsDialog controller split
124
+
125
+ Статус: first controller layer done; full view split deferred.
126
+
127
+ Что сделано:
128
+ - добавлен `useSettingsDialogController(props)`;
129
+ - controller владеет open/close, active/search, search history open/commit/pick/clear, expanded tree state, dotted tree cycle, nav resize state/commit, Escape handling and refs;
130
+ - `SettingsDialog` остался compatibility wrapper with the same JSX/portal/FloatingWindowBase/classes;
131
+ - pure tree/search helpers не переписывались;
132
+ - QA card 20 менять не нужно: он продолжает проверять старый visual wrapper.
133
+
134
+ Что пропущено:
135
+ - full `SettingsDialogView` split по файлам: риск выше пользы сейчас;
136
+ - переписывание `buildSettingsTree`/search helpers, registry, `settingsDialogLayout` store, `FloatingWindowBase`, CSS/tokens/default trigger;
137
+ - любые UX changes search history/divider/dotted tree control ради красивого split.
138
+
139
+ Оценка:
140
+ - эффективность: medium/high;
141
+ - простота: medium;
142
+ - риск: medium, снижен тем, что JSX и UX остались на месте.
143
+
144
+ Проверка:
145
+ - `settingsDialog.test.tsx`: old search/content/history regression + hook open/search/tree-cycle state;
146
+ - QA card 20: search history, outside blur close, dotted tree cycle, divider drag/keyboard/double-click reset, Escape behavior.
147
+ ### 5. Menu action controller + action-level stats
148
+
149
+ Статус: action-level stats done; полный action-controller split отложен как более крупный pass.
150
+
151
+ Что сделано:
152
+ - `MenuItemStrict.actionKey?: string | null` стал стабильным diagnostics-key контрактом;
153
+ - `MenuActionEvent` / `MenuActionHandler` прокинуты через `Menu`, nested menu layers и `contextMenu.Layer`;
154
+ - `contextMenu.stats` теперь считает `actionTotals` и `actions[actionKey]` для click/ok/error/task/submenu/func/focus outcomes;
155
+ - stats не падают обратно на `name`/index/source, не хранят labels и errors;
156
+ - QA card 4 использует `qa4.*` action keys.
157
+
158
+ Что осталось отдельной задачей:
159
+ - выделить `createMenuActionController(...)` или `useMenuAction(...)`, если нужен более тонкий визуальный слой;
160
+ - перенести исполнение item action/progress/loading cleanup из visual components без изменения DOM/classes;
161
+ - добавить расширенные async-error tests, если будет выделяться controller.
162
+
163
+ Оценка выполненного pass:
164
+ - эффективность: medium/high;
165
+ - простота: medium;
166
+ - риск: medium, снижен явным `actionKey` и focused tests.
167
+
168
+ Проверка:
169
+ - unit tests: direct stats, reset/onChange, legacy Layer, keyed action, unkeyed action, keyed submenu;
170
+ - QA card 4: right-click menu open/close/click/submenu behavior.
171
+
172
+ ### 6. FloatingWindow / DnD controller split
173
+
174
+ Статус: first controller layer done; pointer-drag migration deferred.
175
+
176
+ Что сделано:
177
+ - добавлен `useFloatingWindowController(...)` в `FloatingWindow.tsx`;
178
+ - controller владеет saved geometry (`position/size`), update counter, z-index stack, drag active state, mouse/touch header handlers, resize callbacks, viewport clamp and persistence `touch`;
179
+ - экспортированы `FloatingWindowProps`, `FloatingWindowPosition`, `FloatingWindowSize`, `FloatingWindowSavedGeometry`, `FloatingWindowControllerOptions`, `FloatingWindowController`;
180
+ - `FloatingWindowBase` остался compatibility visual wrapper над тем же `Rnd`, с теми же DOM/classes/header/close/overlay paths;
181
+ - `FloatingWindow` memo-child wrapper сохранён;
182
+ - `openWindows` registry уже использует `createUpdateApi`, отдельный `useWindowStack` не выделялся, чтобы не раздувать API без внешнего consumer.
183
+
184
+ Что не сделано сейчас:
185
+ - перенос drag на `useDraggableApi`/pointer-drag не делался: семантика другая (absolute `x/y`, limit clamp, touch id, z-index, persisted map), риск visual regression высокий;
186
+ - `DragBox` не трогался: это отдельный legacy/compat path;
187
+ - `SettingsDialog` JSX/portal/classes не менялись, он продолжает использовать `FloatingWindowBase`.
188
+
189
+ Оценка выполненного pass:
190
+ - эффективность: medium/high;
191
+ - простота: medium/hard;
192
+ - риск: medium, потому что DOM/Rnd/gesture semantics сохранены.
193
+
194
+ Проверка:
195
+ - `floatingWindowController.test.tsx`: restore saved geometry, resize stop commits/touches map, drag clamp and touch, live resize `onUpdate` forwarding;
196
+ - manual QA still needed: card 2 drag/resize/z-index/persisted geometry/no listener spam;
197
+ - card 20 SettingsDialog remains regression point because it uses `FloatingWindowBase`.
198
+
199
+ ### 7. logsContext migration or deprecation
200
+
201
+ Статус: полезно решить направление, но не обязательно переписывать.
202
+
203
+ Что видно сейчас:
204
+ - `logsContext.tsx` содержит свою локальную `memoryGetOrCreate` на `localStorage`;
205
+ - `LogsTable` использует raw `AgGridReact`, хотя основной путь уже перешёл на `AgGridTable`;
206
+ - это выглядит как старый альтернативный logger implementation.
207
+
208
+ Варианты:
209
+ - мигрировать на общий `memoryStore`, `AgGridTable`, `colDefCentered` и общий logs controller;
210
+ - или пометить как deprecated/compat example, чтобы не было двух “правильных” logger API.
211
+
212
+ Оценка:
213
+ - эффективность: medium;
214
+ - простота: hard;
215
+ - риск: medium/high.
216
+
217
+ Почему не делать как quick fix:
218
+ - простая замена `AgGridReact` на `AgGridTable` не решает дублирование logger architecture;
219
+ - прямой `localStorage` лучше убирать вместе с решением о будущем этого модуля.
220
+
221
+ ### 8. RightMenu controller cleanup
222
+
223
+ Статус: hook/controller split done for `DropdownMenu`; deeper registry rewrite skipped as low-effectiveness for now.
224
+
225
+ Что сделано:
226
+ - добавлен `useRightMenuController(...)` для open/fixed/select/submenu/drag-position state;
227
+ - `DropdownMenu` стал compatibility visual wrapper поверх hook/controller state;
228
+ - `createRightMenuController().Render` сохранён и продолжает рендерить `DropdownMenu`;
229
+ - docs и usage examples показывают hook для кастомного DOM, а default wrapper для старого вида.
230
+
231
+ Что пропущено:
232
+ - registry `createRightMenuController` не переводился на `createUpdateApi`: текущий single-render setter уже покрывает реальный consumer/test, а переписывание массива элементов сейчас даст мало пользы при риске совместимости;
233
+ - drag save проверен компиляцией и сохранением старого кода, но без отдельного DOM drag unit-test из-за сложности jsdom geometry.
234
+
235
+ Оценка:
236
+ - эффективность: medium;
237
+ - простота: medium;
238
+ - риск: low/medium после сохранения wrapper API.
239
+
240
+ Проверка:
241
+ - `createRightMenuController` mounted Render rerender;
242
+ - `useRightMenuController` direct open/fixed/select/submenu state;
243
+ - `DropdownMenu` hover submenu and fixed toggle compatibility.
244
+
245
+ ### 9. Resizable primitive hook
246
+
247
+ Статус: skipped for now; low-effectiveness without consumers.
248
+
249
+ Что проверено:
250
+ - `rg` по `src`/`__test` нашёл `FResizableReact` только в export/docs and `mapResiReact` persistence wiring через `memoryStore`;
251
+ - реальных render-consumers, которые проверили бы новый `useResizableSave`, нет.
252
+
253
+ Почему не делать:
254
+ - hook был бы speculative refactor без проверки behavior;
255
+ - `FResizableReact` маленький compatibility wrapper, а риск сломать persisted size/save path выше пользы;
256
+ - возвращаться стоит только когда появится consumer или новая resize-секция в стенде.
257
+
258
+ Оценка:
259
+ - эффективность: low сейчас;
260
+ - простота: medium;
261
+ - риск: medium.
262
+
263
+ ## Задачи, которые сейчас лучше не делать
264
+
265
+ ### memoryStore core helper
266
+
267
+ Статус: пропускать без конкретного test/consumer.
268
+
269
+ Причина:
270
+ - `memoryStore` является core persistence helper для toolbar/columnState/UiSlot/search history/settings/window/right menu flows;
271
+ - `memoryUpdate` можно технически обернуть через `createUpdateApi(cur)`, но это почти не улучшит публичный contract;
272
+ - ошибка здесь ударит сразу по нескольким primitives.
273
+
274
+ Когда возвращаться:
275
+ - если появится test на stored object subscription + `memoryUpdate` + dirty event;
276
+ - если вводим отдельный стандарт “memory object update controller”.
277
+
278
+ ### PageVisibilityProvider
279
+
280
+ Статус: не внедрять в обычный UI ради helper.
281
+
282
+ Причина:
283
+ - полезен только для polling, replay/canvas или тяжёлых анимаций, которые должны паузиться в hidden tab;
284
+ - без конкретного consumer это будет искусственное усложнение.
285
+
286
+ ### StickerMenu
287
+
288
+ Статус: не делать shared-standard pass сейчас.
289
+
290
+ Причина:
291
+ - выглядит как standalone/demo-like side menu с hardcoded items and styles;
292
+ - прежде чем переписывать, нужно решить: это публичный primitive или legacy/demo.
293
+
294
+ Если делать:
295
+ - сначала определить API и usage card;
296
+ - затем уже `useStickerMenuController` + visual wrapper.
297
+
298
+ ### DragBox broad rewrite
299
+
300
+ Статус: не делать вместе с FloatingWindow.
301
+
302
+ Причина:
303
+ - `DragBox` legacy/compat and low-level;
304
+ - новый код лучше вести через `useDraggableApi` или будущий `usePointerDrag`, но существующий compatibility path не ломать без consumer-driven миграции.
305
+
306
+ ## Рекомендуемый порядок выполнения
307
+
308
+ Текущий hook/controller-first backlog из этого audit-pass выполнен настолько, насколько это осмысленно без новых consumers:
309
+
310
+ 1. Logs API/controller split — done.
311
+ 2. SettingsDialog controller split — done.
312
+ 3. FloatingWindow first controller layer — done.
313
+ 4. Resizable hook — skipped until real consumers appear.
314
+
315
+ Дальше не выполнять задачи ради задачи: `memoryStore`, обычный UI + `PageVisibilityProvider`, `StickerMenu`, `DragBox` broad rewrite and `FResizableReact` hook требуют отдельного consumer/test сценария.
316
+
317
+ ## Дополнительный проход: notification layer и новые кандидаты
318
+
319
+ Дата: 2026-07-09.
320
+
321
+ ### MessageEventLogs notification split — done
322
+
323
+ Что сделано:
324
+ - старый `MessageEventLogs` разбит на `useMessageEventLogsController`, `MessageEventLogsView` и `MessageEventLogCard`;
325
+ - `MessageEventLogs` и `logsApi.React.Message` оставлены compatibility wrappers;
326
+ - controller владеет queue/timers/filter by `minVarMessage`, `show`, `setShow`, `toggleShow`, `visibleNotifications`, `maxVisible`;
327
+ - QA card 9 теперь реально монтирует notification layer поверх log card, а не только описывает его в тексте;
328
+ - добавлен focused test `messageEventLogs.test.tsx` и usage docs.
329
+
330
+ Оценка:
331
+ - эффективность: high — убран старый module-level JSX store `tt`, таймеры и подписка вынесены из visual wrapper;
332
+ - простота: medium — глобальный `logsApi`/`datumMiniConst` сохранён ради совместимости;
333
+ - риск: low/medium — старый компонент остался, но уведомления теперь локальны для mounted layer вместо общего module-level React-element map.
334
+
335
+ ### Кандидаты на следующие split passes
336
+
337
+ 1. `menuMouse.tsx` + `menu.tsx`: context menu/action controller split.
338
+ - Идея: выделить `createContextMenuController` / `useMenuLayer` + `MenuView` и отдельный `useMenuAction` для async action/progress/submenu flow.
339
+ - Эффективность: high; простота: medium/hard; риск: medium.
340
+ - Почему не делать мелким quick fix: меню уже имеет compatibility/statistics контракты, нужен focused pass с tests по click/async/submenu/stats.
341
+
342
+ 2. `LeftModal.tsx`: sidebar/modal controller split.
343
+ - Идея: вынести viewport listener, drag API, open/close animation state and imperative store из visual layer.
344
+ - Эффективность: medium/high; простота: medium/hard; риск: medium.
345
+ - Делать, когда появится задача по left/sidebar UX или QA card для ручной приёмки.
346
+
347
+ 3. `logsContext.tsx`: migration/deprecation decision.
348
+ - Идея: не просто дробить файл, а решить направление: общий `logsController`/memoryStore или documented compat surface.
349
+ - Эффективность: medium/high; простота: hard; риск: medium/high.
350
+ - Без решения о будущем context logger переписывание будет косметическим.
351
+
352
+ 4. `chartEngineReact.tsx`: `useChartEngineCanvas` + `ChartCanvasView`.
353
+ - Идея: вынести canvas lifecycle, `ResizeObserver`, event attach/destroy and RAF bridge из React wrapper.
354
+ - Эффективность: medium; простота: medium; риск: medium.
355
+ - Имеет смысл, если будет stand/test на chart canvas lifecycle.
356
+
357
+ 5. `ParamsEditor.tsx`: full row-model/View split.
358
+ - Идея: продолжить после `useParamsEditorController`, но только через отдельную модель строк/inputs.
359
+ - Эффективность: medium/high; простота: hard; риск: high.
360
+ - Сейчас не брать как простую задачу: renderer рекурсивный и чувствителен к array mutation/resize/autostep UX.
361
+
362
+ 6. `SettingsDialog.tsx`: full View split.
363
+ - Идея: controller уже есть; следующий шаг — вынести visual tree/portal/FloatingWindow composition.
364
+ - Эффективность: medium; простота: medium/hard; риск: medium.
365
+ - Делать только вместе с реальным изменением UI, иначе польза ниже риска.
366
+
367
+ Не включать как активные задачи без нового consumer/test: `memoryStore` core rewrite, `StickerMenu`, broad `DragBox`, `FResizableReact` hook.
@@ -0,0 +1,195 @@
1
+ # Hook extraction audit (API-возвращающие хуки)
2
+
3
+ Дата: 2026-07-09.
4
+
5
+ Задача (надиктовка): пройти по всему коду фронт-библиотеки и осмысленно найти места,
6
+ которые стоит вынести в `use*`/`create*` хуки, где хук **возвращает API** — методы,
7
+ которые отдают X, Y, размеры, «сколько», snapshot и т.п. Не «работа ради работы»:
8
+ каждый кандидат обязан устранять реальный императивный код/дублирование и иметь
9
+ реального consumer (QA card / test / app), иначе он отклоняется.
10
+
11
+ Метод: multi-agent проход. 7 finder-агентов по подсистемам (chart, menu, grid,
12
+ dnd-geometry, replay-logs, settings/params/input, utils-core) → на каждый найденный
13
+ кандидат отдельный adversarial critic (проверка фактов grep'ом, сверка с уже
14
+ сделанным и запрещённым списком). Всего 27 агентов, 0 ошибок. Итог: 7 кандидатов
15
+ прошли verify (3 do-now, 4 parked), 13 отклонено.
16
+
17
+ Критерий осмысленности (из надиктовки): хочешь получить X,Y/размер/счётчик →
18
+ правильный хук отдаёт **метод**, который это возвращает (`getSize()`, `getXY()`,
19
+ `toPixel(v)`, `count()`, `isDirty()`). Не рефактор ради стиля.
20
+
21
+ ## Сверка с уже сделанным / запрещённым
22
+
23
+ Проход НЕ переоткрывает уже сделанное: `useKeyboard`, `useMiniLogsTable`,
24
+ `useParamsEditorController`, `useSettingsDialogController`, `useFloatingWindowController`,
25
+ `useRightMenuController`, `createLogsController`/`useLogsTableController`/
26
+ `useLogsNotificationsController`, `useTextInputPanel`/`useFileInputPanel`, section/density
27
+ registries, `createSearchHistory`, `createUiSlot`, `columnState` `createUpdateApi` и др.
28
+ И НЕ предлагает запрещённое без consumer: `FResizableReact` hook, `memoryStore` core
29
+ rewrite, `PageVisibilityProvider` в обычный UI, `StickerMenu` shared primitive, `DragBox`
30
+ broad rewrite. QA card 25 (эталонный toolbar, ~qa.tsx:603) — не трогать.
31
+
32
+ ---
33
+
34
+ ## Прошли verify — DO NOW (3)
35
+
36
+ ### 1. `useResizeObserver` / `useElementSize` — `MyResizeObserver.tsx`
37
+
38
+ - Эффективность: medium; простота: simple; риск: low.
39
+ - Императив сейчас: `CResizeObserver` — это класс + модульный синглтон
40
+ `global_resizeObserver`, чисто side-effect (`add()`/`delete()`), **без React-хука**,
41
+ который бы отдавал измерения.
42
+ - Реальный consumer: `qa.tsx` `ResizeBugRepro` (объявлен ~1076, рендерится ~1723)
43
+ вручную городит `new ResizeObserver` + `rAF` + `getBoundingClientRect` +
44
+ `disconnect` в `useState`. Это **не** эталонная card 25.
45
+ - Также `REFACTOR_PLAN.md` (строки 187/209/221/309) явно просит `useElementSize`
46
+ вместо ручного `MyResizeObserver` цикла.
47
+ - API, который вернёт хук: `{ width, height, getSize(), ref/bind }` — ровно «хочу
48
+ размер → получаю метод/значение».
49
+ - Подход: хук оборачивает существующий синглтон `CResizeObserver`, поэтому
50
+ `setResizeableElement`/вызовы из `ParamsEditor` остаются нетронутыми → малый surface.
51
+ - Оговорки verify: только один чистый consumer (QA card); упомянутые «места
52
+ переиспользования» (FloatingWindow читает x/y для clamp, не размер; chart/agGrid
53
+ имеют свои ResizeObserver, но сложные) чисто не переходят на width/height API —
54
+ не завышать охват.
55
+ - Проверка: `tsc` + `jest`; `ResizeBugRepro` переведён на хук, width/height
56
+ обновляются при ресайзе на стенде; старый `setResizeableElement` путь не сломан.
57
+
58
+ ### 2. `useLogsPageTable` / `useLogsFullTableController` — `logs.tsx`
59
+
60
+ - Эффективность: medium; простота: medium; риск: medium.
61
+ - Императив сейчас: `PageLogs` гоняет ag-grid императивно через `apiGrid` ref;
62
+ блок importance-filter `setFilterModel` **написан дважды** (эффект `updateBy(setting)`
63
+ ~92-103 И `onGridReady` ~160-167); `sizeColumnsToFit()` на update; `applyTransactionAsync`
64
+ append (~105-112).
65
+ - Реальный live consumer: `qa.tsx` card 9 (монтирует `<PageLogs />` ~94; assertion ~1765).
66
+ - Это последняя logs-таблица, не переведённая на controller-паттерн (MiniLogs и
67
+ `logsContext.LogsTable` — отдельные поверхности, уже сделаны).
68
+ - API: `getApi()`, `fit()`, `applyImportanceFilter(min)`, `appendRow(row)`,
69
+ `onGridReady`, `columnDefs`, `gridProps`. Дедуп filter-логики в один метод.
70
+ - Подход: `PageLogs` остаётся тонким wrapper; row identity сохранить.
71
+ - Проверка: card 9 + стенд, особое внимание filter-timing и `applyTransactionAsync` append.
72
+
73
+ ### 3. `useCacheMapPersistence` — `cache.ts`
74
+
75
+ - Эффективность: medium; простота: simple; риск: low.
76
+ - Императив сейчас: тройное **точное** дублирование
77
+ `useEffect(() => { void cache.load(); return cache.onDirty(() => cache.saveDebounced(300)); }, [])`
78
+ в `qa.tsx`: `SettingsDialogDemo` (1221-1224), `UiSlotDemo` (1247-1251),
79
+ `ToolbarDemo` (1329-1332). `ToolbarDemo` (~1320) — отдельная демка, **не** эталонная
80
+ card 25 (та ~603).
81
+ - Совпадает с задокументированным app-контрактом `doc/EXAMPLE_USAGE.md:85-99`,
82
+ `doc/wenay-react2.md:54-63`.
83
+ - API: `{ isDirty(), flush(), save(), reload() }`. Внимание: cache отдаёт `load()`,
84
+ а не `reload()` — `reload()` сделать явным alias/ре-экспортом `load()`, не выдумывать
85
+ несуществующий метод.
86
+ - Границы: аддитивная обёртка, **НЕ** `memoryStore` rewrite; scope строго
87
+ `load` + `onDirty`→`saveDebounced`, без pagehide/visibility flush.
88
+ - Каждый сайт схлопывает ~2-3 строки — польза реальная, но скромная (поэтому
89
+ medium, не high).
90
+ - Проверка: card 20/21 и toolbar demo — persistence переживает reload; card 25 не трогать.
91
+
92
+ ---
93
+
94
+ ## Прошли verify — PARKED (4)
95
+
96
+ Реальны, но сейчас не do-now; причина зафиксирована, вернуть при условии.
97
+
98
+ ### `useChartCanvas` — `myChart/1/myChartTest.tsx`
99
+ - Boilerplate реален (`ChartDemo`: `chartRef`+`containerRef`, `useEffect` с
100
+ `createChartCanvas`/`appendData`/`jumpToEnd`/`setInterval`/`destroy`, кнопки через
101
+ `chartRef.current?.method` null-check). `createChartCanvas` уже сам возвращает
102
+ API-объект — хук лишь добавил бы lifecycle и стабильные non-null методы (паттерн
103
+ как `useFloatingWindowController`). API: `containerRef, appendData, clearData,
104
+ scrollX, zoomX, jumpToStart/End/Index, setAutoScaleY, setShowTimeAxis/PriceAxis, draw()`.
105
+ - **Почему parked**: единственный consumer — сам example-компонент `ChartDemo`, который
106
+ **не отрендерен ни в одной карточке/тесте** → нельзя проверить на стенде (working-style
107
+ требует tsc+стенд). Польза чисто косметическая/демонстрационная.
108
+ - Поправки к автору кандидата: `redraw()` не существует (в `IChartCanvas` это `draw()`);
109
+ `ready` в интерфейсе нет — не тащить в API.
110
+ - Вернуть как do-now, когда `ChartDemo` реально появится в карточке/тесте.
111
+
112
+ ### `useContextMenuGesture` — `menuMouse.tsx`
113
+ - Дублирование реально: gesture-распознавание (порог 0.05, long-press 300ms,
114
+ right/dbl-click) в `menuMouse` `Layer` (~348-380) и в `menuR` `MenuR` (~55-107).
115
+ - **Почему parked**: `menuR.MenuR` — **мёртвый код** (нет app-consumer; заявленный тест
116
+ на самом деле импортит `RightMenu.tsx`, не `menuR`). Единственный live consumer —
117
+ `Layer`. Делить хук с мёртвым кодом — мнимый выигрыш; честный фикс той половины —
118
+ удалить `menuR`. Touch-семантика тонкая, card 4 гоняет только mouse-путь → риск
119
+ touch-регрессии без нового consumer. Это часть уже-parked context-menu controller split.
120
+ - Вернуть при: оживлении `menuR` с touch-тестами ИЛИ новом live touch-consumer.
121
+
122
+ ### `columnState.api.reorderPreview(order, key, to)` — `columnState.ts`
123
+ - Метод на существующем `createColumnState` контроллере (не новый хук): экспонировать
124
+ move+fixed-pinning preview (+опц. `applyFixedPinning(order)`). Тройное дублирование
125
+ идиомы `filter(!fixed)+pinFixed-splice`: `columnState.ts:140-145`,
126
+ `ColumnsMenu.tsx:187-198`, `Toolbar.tsx:448-459`. Реальный consumer — `ColumnsMenu`
127
+ (qa cards 30/31).
128
+ - **Почему parked**: метод дедупит только 1 из 3 копий (копия `Toolbar` — другой
129
+ контроллер над своими `opts.items`, честно вне охвата), и пересекается лишь pin-половина
130
+ → польза low. Брать, если появится общий reorder-контракт для обоих контроллеров.
131
+
132
+ ### `useResizeableFit` — `ParamsEditor.tsx`
133
+ - 4x **байт-идентичный** inline ref-callback (`InputTime` 113-117, `InputList` 175-179,
134
+ `InputNumber` 212-216, `InputListArray` 269-273): `setResizeableElement(el)` +
135
+ `removeResizeableElement` cleanup. Consumer — `ParamsEditor` (qa cards 7 и 19).
136
+ Также чинит minor unmount-dispose leak.
137
+ - **Почему parked / поправка**: это **не** `use*`-хук — все 4 сайта внутри обычных
138
+ функций-хелперов (`InputTime`/`InputList`/`InputNumber`/`InputListArray`), а не
139
+ компонентов, там нельзя вызывать хуки. Правильная форма — общий **module-level
140
+ стабильный bind callback-ref** (`bind(el)` ставит `setResizeableElement`, возвращает
141
+ `()=>removeResizeableElement`). Не пересекается с done (`useParamsEditorController`
142
+ покрывает только draft/notify) и forbidden (`FResizableReact` — другое). low risk,
143
+ узкая польза — можно сделать как маленький shared primitive отдельным микро-passом.
144
+
145
+ ---
146
+
147
+ ## Отклонено (13) — кратко, для покрытия
148
+
149
+ - `useChartEngine` (`chartEngineReact.tsx`) — **parked/не conscious**: императивная
150
+ проводка реальна, но НИ ОДИН consumer не читает обратно координатный API
151
+ (`xToPixel/pixelToValue/getViewport/getCrosshair/getPanelCount`); обе точки
152
+ (`qa.tsx:1707` card 6, `use.tsx:187`) рендерят `<MyChartEngine/>` без пропов и ничего
153
+ не считывают. Координатный хук был бы спекулятивным. Вернуть, если появится стенд/тест
154
+ на chart canvas lifecycle или реальный consumer координат.
155
+ - `useMenuPlacement` (`menu.tsx`) — **skip**: реальный `useLayoutEffect` + `getBoundingClientRect`,
156
+ но ровно один in-file consumer и ноль дублирования; `RightMenu`/`LeftModal` клампят
157
+ по другой семантике (parent rect / другая ось). Извлекать нечего дедуплицировать.
158
+ - `useSidebarMenuController` (`LeftModal.tsx`) — **parked**: движок реален, но заявленные
159
+ consumers ложны (в `use.tsx` — закомментированный блок; `Parameters.tsx` не импортит
160
+ `LeftModal`). Нет live consumer. Совпадает с уже-parked LeftModal split.
161
+ - `useStickerMenuDrag` (`StickerMenu.tsx`) — **skip** (overlap forbidden): drag реален,
162
+ но consumer'ов нет (только barrel export + docs). `StickerMenu` в запрещённом списке.
163
+ - `useColumnDotsGestures` (`ColumnDots.tsx`) — **skip**: жестовая логика реальна, но
164
+ единственный consumer — card 29, второго нет. Извлечение = вью-модель без внешнего
165
+ X/Y/size/count consumer.
166
+ - `useCardModel` (`CardList.tsx`) — **skip**: single-use presentation-деривация (~15 строк),
167
+ без дублирования, consumer только сам `CardList` (card 29). Спекулятивно.
168
+ - `useDragArea` / `getXY on DragArea` (`DragArea.tsx`) — **skip** (overlap): у `DragArea`
169
+ нет render-consumers (только export + docs); дублирует `useDraggable`. Мёртвая поверхность.
170
+ - `createLogsStore` / `useLogsStore` (`logsContext.tsx`) — **parked** (overlap): store
171
+ реален и дублирует `logsController`, но новых consumer'ов нет (только same-file демо +
172
+ 1 unit-test). Это migration/deprecation-решение по `logsContext`, а не быстрый split.
173
+ - `useInputAutoStep` (`ParamsEditor.tsx`) — **parked** (overlap): apply-only DOM behavior,
174
+ 1 consumer (`InputNumber`), геттеров не читает; есть minor unmount-dispose leak (стоит
175
+ починить точечно). Не тянет на API-хук.
176
+ - `useNumericInputDraft` (`ParamsEditor.tsx`) — **parked**: WeakMap-черновик реален, но
177
+ `InputNumber` вызывается как обычная функция (`InputNumber(set,val,range)`, не JSX) →
178
+ хук внутри вызвать нельзя. Блокер до превращения `InputNumber` в компонент.
179
+ - `useCallbackHubEffect` (`callbackHub.ts`) — **skip**: `createCallbackHub.on` уже
180
+ возвращает unsubscribe, годный прямо в `useEffect`; обёртка не нужна; 1 demo-consumer.
181
+ - `useObservableMap` (`observableMap.ts`) — **skip**: чистый 45-строчный data-layer,
182
+ ноль UI-императива/дублирования; подписчики только внутренние + тесты.
183
+ - `usePageVisibility` (`pageVisibilityContext.tsx`) — **parked** (overlap forbidden):
184
+ дублирования нет (`PageVisibilityProvider` уже инкапсулирует listener); хук был бы просто
185
+ `useContext`; consumer'ов ноль. В запрещённом «без consumer» списке.
186
+
187
+ ## Рекомендованный порядок
188
+
189
+ 1. `useCacheMapPersistence` — самый безопасный (simple/low), аддитивный, 3 реальных consumer.
190
+ 2. `useResizeObserver`/`useElementSize` — simple/low, явный запрос в REFACTOR_PLAN + живой consumer.
191
+ 3. `useLogsPageTable` — medium/medium, последняя logs-таблица вне controller-паттерна; делать аккуратно (filter-timing).
192
+
193
+ Каждую задачу — по правилам `doc/target/README.md`: старое имя оставить compatibility
194
+ wrapper, публичный API не менять, проверять `tsc -p tsconfig.qa-check.json --noEmit` +
195
+ `jest` + сборку + стенд. Версию не бампать, не публиковать, не пушить.