wenay-react2 1.0.49 → 1.0.51

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 (54) hide show
  1. package/README.md +14 -14
  2. package/doc/EXAMPLE_USAGE.md +48 -0
  3. package/doc/PROJECT_FUNCTIONALITY.md +13 -2
  4. package/doc/PROJECT_RULES.md +27 -27
  5. package/doc/WENAY_REACT2_RENAMES.md +170 -170
  6. package/doc/changes/v1.0.41.md +35 -35
  7. package/doc/changes/v1.0.42.md +26 -26
  8. package/doc/changes/v1.0.43.md +10 -10
  9. package/doc/changes/v1.0.44.md +14 -14
  10. package/doc/changes/v1.0.45.md +8 -8
  11. package/doc/changes/v1.0.46.md +11 -11
  12. package/doc/changes/v1.0.47.md +9 -9
  13. package/doc/changes/v1.0.48.md +8 -8
  14. package/doc/changes/v1.0.49.md +7 -7
  15. package/doc/changes/v1.0.50.md +7 -0
  16. package/doc/changes/v1.0.51.md +10 -0
  17. package/doc/examples/README.md +1 -1
  18. package/doc/examples/conference-client.html +34 -34
  19. package/doc/examples/conference-client.ts +212 -212
  20. package/doc/examples/conference-server.mjs +150 -150
  21. package/doc/examples/peer-call-media.tsx +7 -7
  22. package/doc/examples/stand.tsx +5 -5
  23. package/doc/native.md +37 -37
  24. package/doc/progress/README.md +13 -13
  25. package/doc/progress/architecture-fix-queue.md +74 -74
  26. package/doc/progress/column-state-present-gate.md +28 -28
  27. package/doc/progress/common2-adoption-1.0.73.md +28 -28
  28. package/doc/progress/common2-adoption-1.0.74.md +24 -24
  29. package/doc/progress/common2-adoption-1.0.75.md +120 -0
  30. package/doc/progress/hook-controller-opportunities.md +363 -363
  31. package/doc/progress/hook-extraction-audit.md +195 -195
  32. package/doc/progress/public-surface-normalization.md +351 -351
  33. package/doc/progress/qa-stand-walkthrough-2026-07-12.md +62 -62
  34. package/doc/progress/stand-as-examples-audit.md +20 -20
  35. package/doc/progress/style-system-normalization.md +121 -121
  36. package/doc/target/README.md +32 -32
  37. package/doc/target/my.md +11 -3
  38. package/doc/wenay-react2-rare.md +11 -7
  39. package/doc/wenay-react2.md +2 -2
  40. package/lib/common/src/grid/agGrid4/core.js +7 -2
  41. package/lib/common/src/hooks/useReplay.js +13 -2
  42. package/lib/common/src/logs/logs.d.ts +4 -1
  43. package/lib/common/src/logs/logs.js +19 -11
  44. package/lib/common/testUseReact/replayVideo.d.ts +5 -0
  45. package/lib/common/testUseReact/replayVideo.js +98 -1
  46. package/lib/style/menuRight.css +19 -19
  47. package/lib/style/style.css +23 -23
  48. package/lib/style/tokens.css +184 -184
  49. package/package.json +54 -54
  50. package/doc/changes/v1.0.35.md +0 -26
  51. package/doc/changes/v1.0.37.md +0 -13
  52. package/doc/changes/v1.0.38.md +0 -48
  53. package/doc/changes/v1.0.39.md +0 -35
  54. package/doc/changes/v1.0.40.md +0 -15
@@ -1,62 +1,62 @@
1
- # QA stand walkthrough — 2026-07-12
2
-
3
- Полный автоматизированный проход всех 43 карточек стенда (28 active + 15 archive) в браузере
4
- после восстановления окружения с нуля. Итог: **все проверяемые контракты подтверждены,
5
- 0 ошибок в консоли за весь проход**; найденные дефекты стенда исправлены (см. ниже).
6
-
7
- ## Окружение (восстановлено с нуля)
8
-
9
- - `pnpm install` (pnpm 9, lockfile 9.0). **Важно:** установка должна идти под Node ≥22.12 —
10
- под Node 21 pnpm молча пропускает `@rolldown/binding-win32-x64-msvc` (engines-фильтр),
11
- и vite 8 не стартует («Cannot find native binding»).
12
- - Системный Node 21.6 (EOL) не подходит для Vite 8: портативный Node 22.23.1 лежит в
13
- `~/.tools/node-v22.23.1-win-x64`, стенд запускается обёрткой `~/.tools/vite-node22.cmd`
14
- (прописана в `.claude/launch.json`).
15
- - `typescript ^5` добавлен в devDependencies (раньше жил только глобально). TS 7 не брать:
16
- ts-jest требует `<7`, tsconfig использует удалённый в TS7 `moduleResolution: node10` —
17
- миграция записана как кандидат в breaking-проход.
18
- - Проверки: tsc qa-check ✅, Jest 23 suites / 78 tests ✅, build ✅.
19
-
20
- ## Результаты по карточкам
21
-
22
- Автоматически подтверждены (клики/жесты синтетикой, чтение DOM/модели):
23
- 1, 3, 4, 5, 7 (sync+t33), 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 2, 20 (поиск/RU-раскладка/
24
- двухступенчатый Escape/реестр), 21 (+F5-персист), 22, 23 (A–E), 24, 25 (+F5, reset), 26, 27,
25
- 28 (двусторонний контракт), 29, 30 (presentGate 4 режима), 31, 32, 33, 34, 35, 40, 41, 42.
26
- Медиа 38/39/43/44/45: deny-путь чистый (idle→denied без крэша), звонок 45 без медиа проходит
27
- ringing→active→idle.
28
-
29
- ## Чек-лист ручной приёмки (нужны глаза/устройства)
30
-
31
- - **6** — амплитуда LOD на зуме (визуально); **23/34** — плавность видео.
32
- - **19** — fixed-parent select при 150/240px (метки на RO+rAF пайплайне).
33
- - **20** — драг разделителя дерева, history-дропдаун; **25/30/31** — drag-reorder строк
34
- настроек и HOLD-превью середины жеста; **26/27** — совпадение превью и коммита глазами.
35
- - **28** — драг заголовков ag-grid и resize (нативный dragService не берётся синтетикой).
36
- - **29/32** — live-замена поля при медленном драге точки + палец-лейбл.
37
- - **9** — всплывающая нотификация (~2 c) и её выключатель.
38
- - **38/39/43/44/45** — реальная камера/микрофон: живое видео на канвасе, эхо голоса,
39
- заморозка счётчиков при ACL-revoke, hidden-tab.
40
-
41
- ## Исправления по итогам прохода
42
-
43
- - qa.tsx: карточка 14 «click clear»→«reset via API»; карточка 29 — снят недостижимый
44
- «Max 4 dots» (демо даёт max=8 на 8 колонок); карточка 38 — ожидание без фантомного
45
- «requesting»; карточка 2 — `onCLickClose`→`onClickClose` (2 места) и обновлена note про
46
- исторический xxx-спам.
47
- - demo/peerMedia.tsx (карточка 45): аудио теперь включается по документированным шагам —
48
- намерение enable запоминается и применяется к плееру, создаваемому после accept.
49
- - logsController.ts: подписи `minVarLogs`/`minVarMessage` были перепутаны местами
50
- (minVarLogs фильтрует таблицу — logs.tsx:149; minVarMessage гейтит нотификации —
51
- logs.tsx:294). Поменяны только name-строки, ключи/семантика не тронуты.
52
-
53
- ## Заметки для будущей автоматизации стенда
54
-
55
- - Панель Claude-браузера не рисует кадры: CSS-переходы замирают → визуальные позиции строк
56
- ag-grid читать по `row-index`/`aria-rowcount`/inline transform, НЕ по getBoundingClientRect.
57
- - ag-grid v35: клик по ячейке требует pointer-событий; заголовки/колонки виртуализуются
58
- горизонтально (скроллить вьюпорт перед чтением).
59
- - ColumnDots: синтетические PointerEvent работают после no-op патча
60
- `Element.prototype.setPointerCapture`.
61
- - useDraggable/useReorder: после mousedown дать тик (~50 мс) на подписку document-слушателей,
62
- mousemove слать с `buttons:1`.
1
+ # QA stand walkthrough — 2026-07-12
2
+
3
+ Полный автоматизированный проход всех 43 карточек стенда (28 active + 15 archive) в браузере
4
+ после восстановления окружения с нуля. Итог: **все проверяемые контракты подтверждены,
5
+ 0 ошибок в консоли за весь проход**; найденные дефекты стенда исправлены (см. ниже).
6
+
7
+ ## Окружение (восстановлено с нуля)
8
+
9
+ - `pnpm install` (pnpm 9, lockfile 9.0). **Важно:** установка должна идти под Node ≥22.12 —
10
+ под Node 21 pnpm молча пропускает `@rolldown/binding-win32-x64-msvc` (engines-фильтр),
11
+ и vite 8 не стартует («Cannot find native binding»).
12
+ - Системный Node 21.6 (EOL) не подходит для Vite 8: портативный Node 22.23.1 лежит в
13
+ `~/.tools/node-v22.23.1-win-x64`, стенд запускается обёрткой `~/.tools/vite-node22.cmd`
14
+ (прописана в `.claude/launch.json`).
15
+ - `typescript ^5` добавлен в devDependencies (раньше жил только глобально). TS 7 не брать:
16
+ ts-jest требует `<7`, tsconfig использует удалённый в TS7 `moduleResolution: node10` —
17
+ миграция записана как кандидат в breaking-проход.
18
+ - Проверки: tsc qa-check ✅, Jest 23 suites / 78 tests ✅, build ✅.
19
+
20
+ ## Результаты по карточкам
21
+
22
+ Автоматически подтверждены (клики/жесты синтетикой, чтение DOM/модели):
23
+ 1, 3, 4, 5, 7 (sync+t33), 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 2, 20 (поиск/RU-раскладка/
24
+ двухступенчатый Escape/реестр), 21 (+F5-персист), 22, 23 (A–E), 24, 25 (+F5, reset), 26, 27,
25
+ 28 (двусторонний контракт), 29, 30 (presentGate 4 режима), 31, 32, 33, 34, 35, 40, 41, 42.
26
+ Медиа 38/39/43/44/45: deny-путь чистый (idle→denied без крэша), звонок 45 без медиа проходит
27
+ ringing→active→idle.
28
+
29
+ ## Чек-лист ручной приёмки (нужны глаза/устройства)
30
+
31
+ - **6** — амплитуда LOD на зуме (визуально); **23/34** — плавность видео.
32
+ - **19** — fixed-parent select при 150/240px (метки на RO+rAF пайплайне).
33
+ - **20** — драг разделителя дерева, history-дропдаун; **25/30/31** — drag-reorder строк
34
+ настроек и HOLD-превью середины жеста; **26/27** — совпадение превью и коммита глазами.
35
+ - **28** — драг заголовков ag-grid и resize (нативный dragService не берётся синтетикой).
36
+ - **29/32** — live-замена поля при медленном драге точки + палец-лейбл.
37
+ - **9** — всплывающая нотификация (~2 c) и её выключатель.
38
+ - **38/39/43/44/45** — реальная камера/микрофон: живое видео на канвасе, эхо голоса,
39
+ заморозка счётчиков при ACL-revoke, hidden-tab.
40
+
41
+ ## Исправления по итогам прохода
42
+
43
+ - qa.tsx: карточка 14 «click clear»→«reset via API»; карточка 29 — снят недостижимый
44
+ «Max 4 dots» (демо даёт max=8 на 8 колонок); карточка 38 — ожидание без фантомного
45
+ «requesting»; карточка 2 — `onCLickClose`→`onClickClose` (2 места) и обновлена note про
46
+ исторический xxx-спам.
47
+ - demo/peerMedia.tsx (карточка 45): аудио теперь включается по документированным шагам —
48
+ намерение enable запоминается и применяется к плееру, создаваемому после accept.
49
+ - logsController.ts: подписи `minVarLogs`/`minVarMessage` были перепутаны местами
50
+ (minVarLogs фильтрует таблицу — logs.tsx:149; minVarMessage гейтит нотификации —
51
+ logs.tsx:294). Поменяны только name-строки, ключи/семантика не тронуты.
52
+
53
+ ## Заметки для будущей автоматизации стенда
54
+
55
+ - Панель Claude-браузера не рисует кадры: CSS-переходы замирают → визуальные позиции строк
56
+ ag-grid читать по `row-index`/`aria-rowcount`/inline transform, НЕ по getBoundingClientRect.
57
+ - ag-grid v35: клик по ячейке требует pointer-событий; заголовки/колонки виртуализуются
58
+ горизонтально (скроллить вьюпорт перед чтением).
59
+ - ColumnDots: синтетические PointerEvent работают после no-op патча
60
+ `Element.prototype.setPointerCapture`.
61
+ - useDraggable/useReorder: после mousedown дать тик (~50 мс) на подписку document-слушателей,
62
+ mousemove слать с `buttons:1`.
@@ -1,22 +1,22 @@
1
- # Stand as examples — audit
2
-
3
- ## Current shape
4
-
5
- - `src/common/testUseReact/qa.tsx` owns 44 hand-written cards and the component implementations are mostly in the same file.
6
- - The board has only Active/Archive separation. It does not declare whether a card is canonical consumer guidance or internal regression/compat coverage.
7
- - `doc/examples/peer-call-media.tsx` is shipped, but card 41–44 currently use a parallel implementation.
8
-
9
- ## Decision
10
-
11
- Turn the stand into a renderer/acceptance layer over a small set of canonical demo modules. Ship those modules in `demo/`; keep isolated repros and compat examples in QA/regression only. Use a manifest per card to link source demo, automated test and manual verification.
12
-
13
- ## First migration
14
-
15
- Extract Media/Peer calls, presence, camera relay and microphone relay. The QA cards must import the same component that is shipped in the package. No new transport/ACL code belongs in React.
16
- ## Batch 1 complete — Media/Peer
17
-
18
- - Extracted cards 41–44 into `src/common/demo/peerMedia.tsx`.
19
- - QA imports those exact components; package exports `wenay-react2/demo/peer-media`.
20
- - `doc/examples/peer-call-media.tsx` re-exports the public demo entrypoint.
1
+ # Stand as examples — audit
2
+
3
+ ## Current shape
4
+
5
+ - `src/common/testUseReact/qa.tsx` owns 44 hand-written cards and the component implementations are mostly in the same file.
6
+ - The board has only Active/Archive separation. It does not declare whether a card is canonical consumer guidance or internal regression/compat coverage.
7
+ - `doc/examples/peer-call-media.tsx` is shipped, but card 41–44 currently use a parallel implementation.
8
+
9
+ ## Decision
10
+
11
+ Turn the stand into a renderer/acceptance layer over a small set of canonical demo modules. Ship those modules in `demo/`; keep isolated repros and compat examples in QA/regression only. Use a manifest per card to link source demo, automated test and manual verification.
12
+
13
+ ## First migration
14
+
15
+ Extract Media/Peer calls, presence, camera relay and microphone relay. The QA cards must import the same component that is shipped in the package. No new transport/ACL code belongs in React.
16
+ ## Batch 1 complete — Media/Peer
17
+
18
+ - Extracted cards 41–44 into `src/common/demo/peerMedia.tsx`.
19
+ - QA imports those exact components; package exports `wenay-react2/demo/peer-media`.
20
+ - `doc/examples/peer-call-media.tsx` re-exports the public demo entrypoint.
21
21
  - Remaining batches: Replay, then toolbar/columnState/grid; archive stays internal.
22
22
  - Verification: qa-check TypeScript, full Jest (21 suites / 71 tests), package build and Vite stand cards 41–44 all pass. The package is browser-oriented; direct Node require is not a supported validation surface for its extensionless ESM build.
@@ -1,121 +1,121 @@
1
- # Style system normalization
2
-
3
- Status: done/released (v1.0.38, v1.0.39, v1.0.41); default inventory and column-state scoped token fixes implemented and shipped.
4
-
5
- ## Source
6
-
7
- User clarification: compatibility can be an aggressive migration when it is
8
- clearly recorded in changelog, but default styles are dangerous to remove.
9
- If default styles disappear, the project can become visually broken even when
10
- the TypeScript/API migration is understandable.
11
-
12
- ## Standard
13
-
14
- - Shared primitives must keep a usable default visual style.
15
- - A default style may be changed, but not silently removed.
16
- - Removing/replacing a default class requires a replacement class/token path,
17
- changelog entry, and migration note.
18
- - New shared visual rules should use CSS custom properties first.
19
- - Runtime geometry may stay inline when it is state-derived, but color, spacing,
20
- border, shadow, font, and hover/active states should move to classes/tokens.
21
- - Apps may override variables; primitives should not require apps to copy a full
22
- CSS block to remain usable.
23
-
24
- ## Token direction
25
-
26
- Current prefixes are a good start:
27
-
28
- - `--color-*`
29
- - `--menu-*`
30
- - `--wnd-*`
31
- - `--dlg-*`
32
- - `--tb-*`
33
- - `--logs-*`
34
- - `--cols-menu-*` for compact `ColumnsMenu/MenuStrip` visuals.
35
- - `--cols-dots-*` for `ColumnDots` visuals.
36
- - `--cols-card-*` for `CardList` visuals.
37
-
38
- Likely next prefixes:
39
-
40
- - `--cols-*` remains a family prefix; concrete prefixes now use `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
41
- - `--input-*` for generic input/panel defaults if they stay public.
42
- - `--param-*` for `ParamsEditor` if it remains a shared primitive.
43
-
44
- ## First audit candidates
45
-
46
- - `src/style/style.css`: old button/menu classes, `FloatingWindow`, `SettingsDialog`, `Toolbar`, `ColumnDots/CardList`, and raw hover colors.
47
- - `src/style/menuRight.css`: right-menu visual contract and default state styles.
48
- - `src/common/src/grid/columnState/*`: finish deciding whether mobile/card
49
- visuals need `--cols-*` tokens.
50
- - `src/common/src/components/ParamsEditor.tsx`: move reusable editor visuals
51
- out of component-owned inline styles if the API remains public.
52
- - `src/common/src/components/Input.tsx`: decide whether old input panels are
53
- compatibility-only or need a proper tokenized default.
54
-
55
- ## Acceptance
56
-
57
- - Default visual contract documented per public primitive group.
58
- - Token prefixes chosen before broad style rewrites.
59
- - No removal of default classes without replacement and changelog.
60
- - Scoped visual QA cards checked before and after style migration.
61
- ## 2026-07-09 default style contract inventory
62
-
63
- Source of truth:
64
-
65
- - CSS tokens: `src/style/tokens.css`.
66
- - TS mirror for inline styles/theme builders: `src/common/src/styles/tokens.ts`.
67
- - Shared runtime CSS: `src/style/style.css` and `src/style/menuRight.css`.
68
- - Generated `dist/lib/style/*` is build output, not the edit target.
69
-
70
- Public/default visual contracts:
71
-
72
- - `Menu` / `MenuR` / mouse context menu: legacy `.MenuR`, `.toButton`, `.toButtonA` classes remain; colors/shadow/borders are already routed through `--menu-*` tokens. Keep the old class names as compatibility surface.
73
- - `RightMenu`: default classes `.menu-container`, `.menu-button`, `.dropdown-content`, `.menu-item`, `.submenu`, `.draggable-div` live in `src/style/menuRight.css` and already consume `--menu-*` / `--menu-outline-color`.
74
- - `FloatingWindow`: `.wenayWnd*` and `.wenayCloseBtn` live in `src/style/style.css`; defaults intentionally preserve the old transparent/striped window while allowing `--wnd-*` theme overrides.
75
- - `SettingsDialog`: `.wenayDlg*` classes live in `src/style/style.css`; default look is dark and tokenized through `--dlg-*` plus selected `--wnd-*` bridge variables on `.wenayDlgWindow`.
76
- - `Toolbar` / `createToolbar`: `.wenayTb*` classes live in `src/style/style.css`; defaults consume `--tb-*`; stand-specific skins should override through wrapper classes, not by changing shared defaults.
77
- - `Logs`: `src/common/src/logs/logStyles.ts` maps inline logger chrome to `--logs-*`; this is an accepted transitional contract while old logger JSX stays inline-heavy.
78
- - `ColumnDots` / `CardList`: `.wenayColDots*` and `.wenayCardList*` classes live in `src/style/style.css`; restored card-29 defaults are now tokenized through `--cols-dots-*` / `--cols-card-*` with the same fallback values.
79
- - `ColumnsMenu` / `MenuStrip`: behavior is already headless/presentation split. Visual button/container/divider states now use `.wenayColsMenu*` classes and `--cols-menu-*` tokens with the exact previous GitHub-like defaults; runtime reorder transform stays inline.
80
- - `ParamsEditor`: public and still mostly old class + inline style based (`toIndicatorMenuButton`, `miniEl`, `toButtonEasy`, `inputCan`). Do not token-migrate broadly until hook/API direction is decided; candidate prefix `--param-*`.
81
- - `Input` modal helpers: public compatibility helpers over `FloatingWindow`, `.msTradeAlt`, `.msTradeActive`, `.inputCan`, inline panel spacing. Candidate prefix `--input-*`, but may remain compatibility-only if newer app surfaces replace them.
82
-
83
- Done scoped fix:
84
-
85
- - `ColumnsMenu/MenuStrip` visual button/container/divider styles moved to classes/tokens; runtime reorder transform/drag geometry remains inline.
86
-
87
- Done scoped fixes:
88
-
89
- - `ColumnsMenu/MenuStrip` visual button/container/divider styles moved to classes/tokens; runtime reorder transform/drag geometry remains inline.
90
- - `ColumnDots/CardList` restored card-29 defaults moved to `--cols-dots-*` / `--cols-card-*` variables with unchanged fallback values.
91
-
92
- Next scoped candidate:
93
-
94
- - Leave `ParamsEditor` / `Input` broad visual migration until the hook-first/API direction is clearer; only do bug-level style fixes there.
95
- ## 2026-07-09 scoped fix - ColumnsMenu/MenuStrip tokens
96
-
97
- Implemented the compact column menu style contract:
98
-
99
- - Added `--cols-menu-*` defaults to `src/style/tokens.css` and the TS mirror in `src/common/src/styles/tokens.ts`.
100
- - Added `.wenayColsMenu*` classes in `src/style/style.css` for container/list/button states, fixed/drag shadow, text abbreviation, marks, and divider.
101
- - Updated `src/common/src/grid/columnState/ColumnsMenu.tsx` so visual state uses classes; only consumer `style` and `useReorder` runtime transform remain inline.
102
-
103
- Verification: `npx tsc -p tsconfig.qa-check.json --noEmit`; `npm run testjest -- --runInBand`; `npm run build`; `git diff --check`.
104
- ## 2026-07-09 scoped fix - ColumnDots/CardList tokens
105
-
106
- Implemented the mobile/card column-state style contract:
107
-
108
- - Added `--cols-dots-*` and `--cols-card-*` defaults to `src/style/tokens.css` and the TS mirror in `src/common/src/styles/tokens.ts`.
109
- - Rewrote `.wenayColDots*` and `.wenayCardList*` values in `src/style/style.css` to use variables with the exact previous fallbacks.
110
- - Kept component code unchanged; `ColumnDots` runtime geometry (`left`, drag transform) remains inline in the component where it belongs.
111
-
112
- Verification: `npx tsc -p tsconfig.qa-check.json --noEmit`; `npm run testjest -- --runInBand`; `npm run build`; `git diff --check`.
113
- ## 2026-07-09 — Menu default contrast restore
114
-
115
- Сделано:
116
- - `--menu-bg-color` возвращён к непрозрачному dark fallback `#0c0c0c`, `--menu-blur` по умолчанию `0px`;
117
- - добавлены `--menu-item-active-*` и `--menu-item-pressed-*` tokens, старый `--menu-active-background` оставлен alias/fallback;
118
- - `MenuR` / mouse context menu and `RightMenu` CSS получили hard fallbacks: default dark background, white text, hover white with dark text, pressed yellow-ish contrast state;
119
- - причина: рабочие проекты могут ещё не переопределять новые CSS variables, поэтому дефолт не должен становиться glass/transparent.
120
-
121
- Проверка: CSS-only visual contract; прогнать `tsc`/`build`, card 4 вручную открыть правой кнопкой.
1
+ # Style system normalization
2
+
3
+ Status: done/released (v1.0.38, v1.0.39, v1.0.41); default inventory and column-state scoped token fixes implemented and shipped.
4
+
5
+ ## Source
6
+
7
+ User clarification: compatibility can be an aggressive migration when it is
8
+ clearly recorded in changelog, but default styles are dangerous to remove.
9
+ If default styles disappear, the project can become visually broken even when
10
+ the TypeScript/API migration is understandable.
11
+
12
+ ## Standard
13
+
14
+ - Shared primitives must keep a usable default visual style.
15
+ - A default style may be changed, but not silently removed.
16
+ - Removing/replacing a default class requires a replacement class/token path,
17
+ changelog entry, and migration note.
18
+ - New shared visual rules should use CSS custom properties first.
19
+ - Runtime geometry may stay inline when it is state-derived, but color, spacing,
20
+ border, shadow, font, and hover/active states should move to classes/tokens.
21
+ - Apps may override variables; primitives should not require apps to copy a full
22
+ CSS block to remain usable.
23
+
24
+ ## Token direction
25
+
26
+ Current prefixes are a good start:
27
+
28
+ - `--color-*`
29
+ - `--menu-*`
30
+ - `--wnd-*`
31
+ - `--dlg-*`
32
+ - `--tb-*`
33
+ - `--logs-*`
34
+ - `--cols-menu-*` for compact `ColumnsMenu/MenuStrip` visuals.
35
+ - `--cols-dots-*` for `ColumnDots` visuals.
36
+ - `--cols-card-*` for `CardList` visuals.
37
+
38
+ Likely next prefixes:
39
+
40
+ - `--cols-*` remains a family prefix; concrete prefixes now use `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
41
+ - `--input-*` for generic input/panel defaults if they stay public.
42
+ - `--param-*` for `ParamsEditor` if it remains a shared primitive.
43
+
44
+ ## First audit candidates
45
+
46
+ - `src/style/style.css`: old button/menu classes, `FloatingWindow`, `SettingsDialog`, `Toolbar`, `ColumnDots/CardList`, and raw hover colors.
47
+ - `src/style/menuRight.css`: right-menu visual contract and default state styles.
48
+ - `src/common/src/grid/columnState/*`: finish deciding whether mobile/card
49
+ visuals need `--cols-*` tokens.
50
+ - `src/common/src/components/ParamsEditor.tsx`: move reusable editor visuals
51
+ out of component-owned inline styles if the API remains public.
52
+ - `src/common/src/components/Input.tsx`: decide whether old input panels are
53
+ compatibility-only or need a proper tokenized default.
54
+
55
+ ## Acceptance
56
+
57
+ - Default visual contract documented per public primitive group.
58
+ - Token prefixes chosen before broad style rewrites.
59
+ - No removal of default classes without replacement and changelog.
60
+ - Scoped visual QA cards checked before and after style migration.
61
+ ## 2026-07-09 default style contract inventory
62
+
63
+ Source of truth:
64
+
65
+ - CSS tokens: `src/style/tokens.css`.
66
+ - TS mirror for inline styles/theme builders: `src/common/src/styles/tokens.ts`.
67
+ - Shared runtime CSS: `src/style/style.css` and `src/style/menuRight.css`.
68
+ - Generated `dist/lib/style/*` is build output, not the edit target.
69
+
70
+ Public/default visual contracts:
71
+
72
+ - `Menu` / `MenuR` / mouse context menu: legacy `.MenuR`, `.toButton`, `.toButtonA` classes remain; colors/shadow/borders are already routed through `--menu-*` tokens. Keep the old class names as compatibility surface.
73
+ - `RightMenu`: default classes `.menu-container`, `.menu-button`, `.dropdown-content`, `.menu-item`, `.submenu`, `.draggable-div` live in `src/style/menuRight.css` and already consume `--menu-*` / `--menu-outline-color`.
74
+ - `FloatingWindow`: `.wenayWnd*` and `.wenayCloseBtn` live in `src/style/style.css`; defaults intentionally preserve the old transparent/striped window while allowing `--wnd-*` theme overrides.
75
+ - `SettingsDialog`: `.wenayDlg*` classes live in `src/style/style.css`; default look is dark and tokenized through `--dlg-*` plus selected `--wnd-*` bridge variables on `.wenayDlgWindow`.
76
+ - `Toolbar` / `createToolbar`: `.wenayTb*` classes live in `src/style/style.css`; defaults consume `--tb-*`; stand-specific skins should override through wrapper classes, not by changing shared defaults.
77
+ - `Logs`: `src/common/src/logs/logStyles.ts` maps inline logger chrome to `--logs-*`; this is an accepted transitional contract while old logger JSX stays inline-heavy.
78
+ - `ColumnDots` / `CardList`: `.wenayColDots*` and `.wenayCardList*` classes live in `src/style/style.css`; restored card-29 defaults are now tokenized through `--cols-dots-*` / `--cols-card-*` with the same fallback values.
79
+ - `ColumnsMenu` / `MenuStrip`: behavior is already headless/presentation split. Visual button/container/divider states now use `.wenayColsMenu*` classes and `--cols-menu-*` tokens with the exact previous GitHub-like defaults; runtime reorder transform stays inline.
80
+ - `ParamsEditor`: public and still mostly old class + inline style based (`toIndicatorMenuButton`, `miniEl`, `toButtonEasy`, `inputCan`). Do not token-migrate broadly until hook/API direction is decided; candidate prefix `--param-*`.
81
+ - `Input` modal helpers: public compatibility helpers over `FloatingWindow`, `.msTradeAlt`, `.msTradeActive`, `.inputCan`, inline panel spacing. Candidate prefix `--input-*`, but may remain compatibility-only if newer app surfaces replace them.
82
+
83
+ Done scoped fix:
84
+
85
+ - `ColumnsMenu/MenuStrip` visual button/container/divider styles moved to classes/tokens; runtime reorder transform/drag geometry remains inline.
86
+
87
+ Done scoped fixes:
88
+
89
+ - `ColumnsMenu/MenuStrip` visual button/container/divider styles moved to classes/tokens; runtime reorder transform/drag geometry remains inline.
90
+ - `ColumnDots/CardList` restored card-29 defaults moved to `--cols-dots-*` / `--cols-card-*` variables with unchanged fallback values.
91
+
92
+ Next scoped candidate:
93
+
94
+ - Leave `ParamsEditor` / `Input` broad visual migration until the hook-first/API direction is clearer; only do bug-level style fixes there.
95
+ ## 2026-07-09 scoped fix - ColumnsMenu/MenuStrip tokens
96
+
97
+ Implemented the compact column menu style contract:
98
+
99
+ - Added `--cols-menu-*` defaults to `src/style/tokens.css` and the TS mirror in `src/common/src/styles/tokens.ts`.
100
+ - Added `.wenayColsMenu*` classes in `src/style/style.css` for container/list/button states, fixed/drag shadow, text abbreviation, marks, and divider.
101
+ - Updated `src/common/src/grid/columnState/ColumnsMenu.tsx` so visual state uses classes; only consumer `style` and `useReorder` runtime transform remain inline.
102
+
103
+ Verification: `npx tsc -p tsconfig.qa-check.json --noEmit`; `npm run testjest -- --runInBand`; `npm run build`; `git diff --check`.
104
+ ## 2026-07-09 scoped fix - ColumnDots/CardList tokens
105
+
106
+ Implemented the mobile/card column-state style contract:
107
+
108
+ - Added `--cols-dots-*` and `--cols-card-*` defaults to `src/style/tokens.css` and the TS mirror in `src/common/src/styles/tokens.ts`.
109
+ - Rewrote `.wenayColDots*` and `.wenayCardList*` values in `src/style/style.css` to use variables with the exact previous fallbacks.
110
+ - Kept component code unchanged; `ColumnDots` runtime geometry (`left`, drag transform) remains inline in the component where it belongs.
111
+
112
+ Verification: `npx tsc -p tsconfig.qa-check.json --noEmit`; `npm run testjest -- --runInBand`; `npm run build`; `git diff --check`.
113
+ ## 2026-07-09 — Menu default contrast restore
114
+
115
+ Сделано:
116
+ - `--menu-bg-color` возвращён к непрозрачному dark fallback `#0c0c0c`, `--menu-blur` по умолчанию `0px`;
117
+ - добавлены `--menu-item-active-*` и `--menu-item-pressed-*` tokens, старый `--menu-active-background` оставлен alias/fallback;
118
+ - `MenuR` / mouse context menu and `RightMenu` CSS получили hard fallbacks: default dark background, white text, hover white with dark text, pressed yellow-ish contrast state;
119
+ - причина: рабочие проекты могут ещё не переопределять новые CSS variables, поэтому дефолт не должен становиться glass/transparent.
120
+
121
+ Проверка: CSS-only visual contract; прогнать `tsc`/`build`, card 4 вручную открыть правой кнопкой.
@@ -1,33 +1,33 @@
1
- # Target Backlog Rules
2
-
3
- `doc/target` is the durable task backlog for dictated or imported work. It is not a scratchpad and it is not a changelog.
4
-
5
- ## Files
6
-
7
- - `doc/target/my.md` is the user-owned inbox/queue for dictated tasks. Keep it clean and actionable.
8
- - `target/*.md` are source prompt files imported into this repo. Treat them as task specs; do not edit or delete them just to mark progress unless the user asks for that exact cleanup.
9
- - `doc/progress/*.md` are temporary working notes for an active task. They may be deleted when the task is finished, so they must never be the only place where queue status lives.
10
- - `doc/changes/vX.Y.Z.md` records durable completed changes when code, public docs, theme contracts, dependencies, or policy changed.
11
-
12
- ## Workflow
13
-
14
- 1. When the user says to start from `target`, read this README first, then `doc/target/my.md`, then relevant files under `target/`.
15
- 2. Before coding, normalize dictated text into concrete tasks in `doc/target/my.md`: a short title, current status, source note, and acceptance checks if they are known.
16
- 3. When taking a task, move it to `In Progress` in `doc/target/my.md` and point to the progress file if one is created.
17
- 4. Use `doc/progress/<task>.md` only for checkpoints, decisions, blockers, and verification while work is active.
18
- 5. When implementation is believed complete, move the task to `Verify` in `doc/target/my.md` with the concrete checks that prove it: commands, QA stand checks, screenshots, or manual acceptance points.
19
- 6. Remove a task from `doc/target/my.md` only after `Verify` checks pass and the durable result exists: code/docs are updated, verification is recorded, and changelog is updated when required.
20
- 7. If the task is paused or blocked, leave it in `doc/target/my.md` under `In Progress` or `Blocked` with the next concrete action. Do not delete the progress file in that case.
21
- 8. If a task from `target/*.md` is found already implemented, move it to `Verify` first and check its acceptance criteria before removing it. Do not rely on a deleted progress file as proof.
22
-
23
- ## Status Sections For `my.md`
24
-
25
- Use these sections:
26
-
27
- - `Inbox` - raw or lightly cleaned tasks not yet triaged.
28
- - `Ready` - concrete tasks that can be started.
29
- - `In Progress` - the task currently being worked on, with a progress-file link when applicable.
30
- - `Verify` - implementation is believed complete, but acceptance checks still need to be run or reviewed.
31
- - `Blocked` - tasks waiting for user input, another repository, credentials, or an external change.
32
-
1
+ # Target Backlog Rules
2
+
3
+ `doc/target` is the durable task backlog for dictated or imported work. It is not a scratchpad and it is not a changelog.
4
+
5
+ ## Files
6
+
7
+ - `doc/target/my.md` is the user-owned inbox/queue for dictated tasks. Keep it clean and actionable.
8
+ - `target/*.md` are source prompt files imported into this repo. Treat them as task specs; do not edit or delete them just to mark progress unless the user asks for that exact cleanup.
9
+ - `doc/progress/*.md` are temporary working notes for an active task. They may be deleted when the task is finished, so they must never be the only place where queue status lives.
10
+ - `doc/changes/vX.Y.Z.md` records durable completed changes when code, public docs, theme contracts, dependencies, or policy changed.
11
+
12
+ ## Workflow
13
+
14
+ 1. When the user says to start from `target`, read this README first, then `doc/target/my.md`, then relevant files under `target/`.
15
+ 2. Before coding, normalize dictated text into concrete tasks in `doc/target/my.md`: a short title, current status, source note, and acceptance checks if they are known.
16
+ 3. When taking a task, move it to `In Progress` in `doc/target/my.md` and point to the progress file if one is created.
17
+ 4. Use `doc/progress/<task>.md` only for checkpoints, decisions, blockers, and verification while work is active.
18
+ 5. When implementation is believed complete, move the task to `Verify` in `doc/target/my.md` with the concrete checks that prove it: commands, QA stand checks, screenshots, or manual acceptance points.
19
+ 6. Remove a task from `doc/target/my.md` only after `Verify` checks pass and the durable result exists: code/docs are updated, verification is recorded, and changelog is updated when required.
20
+ 7. If the task is paused or blocked, leave it in `doc/target/my.md` under `In Progress` or `Blocked` with the next concrete action. Do not delete the progress file in that case.
21
+ 8. If a task from `target/*.md` is found already implemented, move it to `Verify` first and check its acceptance criteria before removing it. Do not rely on a deleted progress file as proof.
22
+
23
+ ## Status Sections For `my.md`
24
+
25
+ Use these sections:
26
+
27
+ - `Inbox` - raw or lightly cleaned tasks not yet triaged.
28
+ - `Ready` - concrete tasks that can be started.
29
+ - `In Progress` - the task currently being worked on, with a progress-file link when applicable.
30
+ - `Verify` - implementation is believed complete, but acceptance checks still need to be run or reviewed.
31
+ - `Blocked` - tasks waiting for user input, another repository, credentials, or an external change.
32
+
33
33
  Completed tasks are normally removed from `my.md`; the final response, commit message, and changelog carry the durable completion record.
package/doc/target/my.md CHANGED
@@ -2,9 +2,17 @@
2
2
 
3
3
  Правила ведения: [README.md](README.md). Ниже — статус по задачам; **исходные надиктовки сохранены дословно в конце файла** (ничего не удаляю).
4
4
 
5
- ## In Progress
6
-
7
- - **Конференц-демо: групповой созвон на обеих технологиях relay-мост + WebRTC direct.** DONE 2026-07-13 (обе надиктовки 2026-07-12, дословно в конце файла).
5
+ ## In Progress
6
+
7
+ - **Reliability sweep 2026-07-14: replay keyframes and bounded grid delivery.** Source: dictated priority list.
8
+ - `clientBacktest/PanicPanel`: resume `OrderEvents` from the retained replay window/keyframe, never hard-code `since: 0`; opening after the 256-event retention window must recover.
9
+ - `serverAdmin balances`: publish a complete current-state keyframe and recover after journal gaps; a newly connected client must see balances before another event arrives.
10
+ - `wenay-react2 Grid overlay` — implemented: merge a `Partial<Row>` patch with the grid-owned row before sending an AG Grid update, so static fields survive.
11
+ - `wenay-react2 LogsPage` — implemented: remove rows displaced by the bounded controller feed from the live grid; the visible grid stays bounded.
12
+ - Local verification: overlay regression test, full Jest 27/92, `tsc -p tsconfig.qa-check.json --noEmit`, build, `git diff --check`; QA stand is running on `http://127.0.0.1:3010/`.
13
+ - Remaining: targeted recovery proof for `PanicPanel` and the coordinated serverAdmin/client balance-keyframe contract.
14
+
15
+ - **Конференц-демо: групповой созвон на обеих технологиях — relay-мост + WebRTC direct.** DONE 2026-07-13 (обе надиктовки 2026-07-12, дословно в конце файла).
8
16
  - Отгружено: `wenay-react2/demo/peer-conference` — headless `createConferenceWorld` (host-star: групповой звонок КОМПОНУЕТСЯ из парных, один CallManager держит N-1 исходящих; roster активных звонков = единственный ACL-авторитет) + `ConferenceCallDemo` (QA card 46: грид на `Peer.createMediaRelay` fan-out, focus-пара на `Replay.createRouteCoordinator` — relay-hop `serveReplayChannel` и WebRTC `createWebRtcConnector`/`acceptWebRtcDirect` обслуживают ОДНУ owner-seq линию, hand-off без разрывов по seq; никогда не сервить маршрут из `relay.watchOf` — per-watcher seq). Хук `useRouteState`. Fake-RTC loopback (порт оракула common2) для jest/песочниц.
9
17
  - **Через бэк (надиктовка №2)**: `doc/examples/conference-server.mjs` (Node+Socket.IO: peer host, комнатная политика — accepted call вступает обеими сторонами в room, offer брокерится только внутри общей комнаты, media relay с `canWatch`=sameRoom) + `conference-client.html/.ts` (одно место = одна вкладка: грид через серверный relay, peer-store строки с promoteDirect в настоящий cross-tab RTCPeerConnection и reinterpose обратно).
10
18
  - Проверено: tsc, Jest 25/87 (9 новых), build; карточка 46 пройдена живьём на РЕАЛЬНОМ RTCPeerConnection (promote #153→#174 без разрыва, policy-отказы, server revoke, kill transport → auto-fallback); бэкенд-вариант — две вкладки, cross-tab direct (beat 47→51→69 gap-free), сервер переживает mid-room disconnect. Durable: `doc/changes/v1.0.49.md`, brief-док §Route, examples README.
@@ -444,7 +444,8 @@ colDefWrap
444
444
  Important agGrid4 contracts:
445
445
  ```
446
446
  mirror mode: buffer owns row set; sync can add/update/remove grid rows.
447
- overlay mode: rowData owns row set; sync updates only existing grid rows.
447
+ overlay mode: rowData owns row set; sync updates only existing grid rows.
448
+ Partial patches merge with that current row before delivery.
448
449
  clean(): clears buffer; mirror also removes grid rows, overlay does not remove declarative rows.
449
450
  flush(): React/controller method that calls ag-grid flushAsyncTransactions().
450
451
  ```
@@ -554,6 +555,9 @@ useReplayHistory(history, apply, {head?, reset?, tickMs?=300, autoPlay?=true})
554
555
  -> {live, seq, head, pause(), play(), seek({seq?|ts?})}
555
556
  ```
556
557
  Semantics that are easy to get wrong:
558
+ - **Identity and reconnect boundary (common2 1.0.75).** `remote` identifies one logical Replay line. A temporary Socket.IO disconnect/reconnect keeps that object identity, so the React effect stays mounted while common2 rebinds the physical Listen subscription, restores live delivery first, catches up from its private `lastDelivered seq`, drains the racing queue, and deduplicates. Do not call `restart()`, remount with a new key, replace `remote`, poll, or attach transport listeners in React. The retained journal must cover the cursor; a sacred-line eviction with no keyframe is terminal `error`/`onError`, never a silent reset to `since: 0` or latest. `policy: 'queue'` is lossless and must deliver every retained event exactly once; `policy: 'frame'` is explicitly lossy/conflated and is not a journal-integrity check.
559
+ **Identity matrix**
560
+ - **Hard teardown is intentional.** `client.close()`/`dispose()` and hub `connect()`/`setToken()` end the old logical generation. React must clean up normally and must not resurrect that subscription automatically; after an intentional new client or credentials generation, provide its new `remote` as a normal new subscription. Ordinary RPC/pipe calls are not replayed by this contract.
557
561
  - `cb`/`apply` go through refs: new function identity does NOT resubscribe. Resubscribe identity is `[remote, enabled, epoch, staleMs, policy]` — changing `staleMs` resubscribes (it is subscribe-time config in common2; under keepSeq the reconnect is a journal tail, so it is cheap); `policy` picks the wire surface (line vs frameLine), so it resubscribes too. `onStale` goes through a ref like `cb`.
558
562
  - Route hand-off hooks (`useReplayRouteSubscribe`, `useStoreReplayRouteSync`, `useStoreReplayRouteMirror`) wrap common2 `Replay.replayRouteSubscribe` / `Observe.syncStoreReplayRoute`: `switchRoute(nextRemote, {label?, since?, reset?, policy?, hint?})` keeps the old route live, catches up the replacement from the last delivered `seq`, then closes the old route. Failed replacement leaves the old route active and surfaces `error` / `route.phase == "error"`. Changing the `remote` prop is still treated as a fresh subscription boundary; no-gap promotion/demotion is explicit through `switchRoute()`. common2 1.0.65 route handles expose `seq()`, `label()`, and `active()`, but not `isStale()` / `lastTs()`, so route hooks deliberately do not promise freshness yet.
559
563
  - Lag policy / hint (frame model, common2 rev2): `policy: 'frame'` rides the server's `frameLine` when the remote has one — on lag the server drops for THIS client and recovers via the line's `frame` lambda (mini-frame); without a frameLine (old server, in-proc `exposeReplay`) common2 silently degrades to `'queue'`, so an in-proc QA of `'frame'` proves nothing — the wire test lives in common2 (`replay/rpc-auto.test.ts`). `hint` is opaque to the transport and goes through a ref: in `useReplaySubscribe` it is captured at subscribe time (used for the catch-up `frame()` call); in `useReplayFrame` it is read on EVERY pull, so a new identity is never missed and never resubscribes.
@@ -606,16 +610,16 @@ MainPage()
606
610
 
607
611
  The context logger is a larger UI surface; the global `logsApi` is still the shorter integration point. `createLogsController` is the headless layer for append/limit/settings state; `useMessageEventLogsController` owns the global notification queue/timers/settings, while `MessageEventLogsView` and `MessageEventLogCard` own rendering. `PageLogs`, `MessageEventLogs`, and `LogsPage` remain compatibility UI wrappers. `useLogsTableController` and `useLogsNotificationsController` expose the provider-local table/notification state while `LogsTable` and `LogsNotifications` keep the visual wrappers. Shared logger chrome lives in `src/common/src/logs/logStyles.ts` and is themed through `--logs-*` tokens.
608
612
 
609
- Full-page table controller (`useLogsPageTable` -> `LogsPageTableController`): the last logs table
610
- moved to the controller pattern. Semantics that matter: the grid receives a MOUNT-TIME snapshot of
611
- the accumulated log map once (`useState` initializer) and afterwards lives on
612
- `applyTransactionAsync` appends from the mini feed - re-renders do not re-feed `rowData`, so row
613
- identity is per-append copy exactly as before. The importance filter is ONE method
613
+ Full-page table controller (`useLogsPageTable` -> `LogsPageTableController`): the grid receives a
614
+ MOUNT-TIME snapshot of the accumulated log map once (`useState` initializer), then reconciles
615
+ `applyTransactionAsync` adds AND removes with the controller's bounded full map; re-renders do not
616
+ re-feed `rowData`. `num` is the stable grid row id, so a per-source retention eviction removes the
617
+ same record from AG Grid instead of allowing the live table to grow forever. The importance filter is ONE method
614
618
  (`applyImportanceFilter(min?)`: set -> `setFilterModel` greaterThanOrEqual on `var`, unset ->
615
619
  `destroyFilter`), used by both the settings subscription and `onGridReady` (which skips the
616
620
  destroy branch - a fresh grid has no filter). Both data subscriptions are `updateBy(obj, cb)`
617
621
  WITH a callback: they run imperatively on `renderBy` and never re-render the component.
618
- `gridProps` is one stable memo - spread it into `AgGridTable`; `fit()` is exposed for the legacy
622
+ `gridProps` is one stable memo - spread it into `AgGridTable`; `fit()` is exposed for the legacy
619
623
  `update` prop, which `PageLogs` still honors. QA card 9.
620
624
 
621
625
  ## Cache / Memory / Browser Utilities
@@ -478,7 +478,7 @@ Conference composition ships as a live example: `wenay-react2/demo/peer-conferen
478
478
 
479
479
  Media lines (common2 1.0.66 `Media.createAudioSource` / `Media.createVideoSource`) are ordinary binary Listen sources; with `replay:true` their `listen` is a replay line, so `useReplaySubscribe` / `useReplayFrame` consume mic/camera frames with no media-specific hook. `useMediaSource(kind, options)` is only the capture lifecycle adapter (`start`, `stop`, device selection, state and stats); it stops a started source on unmount and returns `listen` unchanged. Each frame is one `Uint8Array` (`Media.decodeMediaFrame`); draw/play it via ref (canvas, AudioContext), never useState — the same rule as any high-frequency line. Without `replay`, the plain `listen` works with the listen hooks above.
480
480
 
481
- Contract: `off()` on unmount, StrictMode-safe; seq survives resubscribes inside one mount (keepSeq, default on) — a resubscribe reconnects with `{since}` and gets the journal tail, not a keyframe. Across a FULL unmount/remount keep the position outside via `onSeq` and pass it back as `since`. `seq()` is a getter — high-frequency lines (video frames, ticks) do not re-render per event; draw to canvas via ref, bypassing VDOM. Freshness: detection lives in wenay-common2 (`staleMs` watchdog); the non-route hooks mirror its edge-triggered `onStale` into `stale`, so a fresh 100 ev/s line causes zero extra renders. Route hand-off is explicit through `switchRoute(nextRemote, {label?, since?, reset?, policy?, hint?})`: old route stays live while the replacement catches up by `seq`, then closes. `useReplayHistory` is archive playback — staleness does not apply. QA cards 23 (video line + conflation + time travel + freshness), 24 (store sync), 25 (per-key feed), and 26 (route hand-off) are the live examples.
481
+ Contract: `off()` on unmount, StrictMode-safe; seq survives resubscribes inside one mount (keepSeq, default on) — a resubscribe reconnects with `{since}` and gets the journal tail, not a keyframe. Across a FULL unmount/remount keep the position outside via `onSeq` and pass it back as `since`. With `wenay-common2@^1.0.75`, a temporary RPC transport disconnect/reconnect keeps the same logical `remote`: common2 rebinds its physical Listen subscription, catches up from its own last delivered seq, and deduplicates racing live events. React must not call `restart()`, remount, change a key, or add Socket.IO listeners for that case. `policy: "queue"` is the lossless choice; `"frame"` is deliberately conflated. A retained-history gap without a keyframe is a terminal `error`, not a fresh start. Deliberate `client.dispose()`/`close()` and hub `connect()`/`setToken()` are hard teardown boundaries, not auto-reconnects. `seq()` is a getter — high-frequency lines (video frames, ticks) do not re-render per event; draw to canvas via ref, bypassing VDOM. Freshness: detection lives in wenay-common2 (`staleMs` watchdog); the non-route hooks mirror its edge-triggered `onStale` into `stale`, so a fresh 100 ev/s line causes zero extra renders. Route hand-off is explicit through `switchRoute(nextRemote, {label?, since?, reset?, policy?, hint?})`: old route stays live while the replacement catches up by `seq`, then closes. `useReplayHistory` is archive playback — staleness does not apply. QA cards 23 (video line + conflation + time travel + freshness), 24 (store sync), 25 (per-key feed), and 26 (route hand-off) are the live examples.
482
482
 
483
483
  ## Logs
484
484
  ```
@@ -507,7 +507,7 @@ const table = useLogsPageTable() // full-page logs grid controll
507
507
  table.fit(); table.applyImportanceFilter(min?); table.appendRow(row); table.getApi()
508
508
  ```
509
509
 
510
- Logger notification/tabs chrome uses `--logs-*` CSS variables; apps can theme it without forking logger components. `createLogsController` owns the headless append/limit/settings state for custom integrations. `useMessageEventLogsController` owns the global notification queue/timers/settings, `MessageEventLogsView` draws it, and `MessageEventLogs` / `logsApi.React.Message` remain compatibility wrappers. `MiniLogs` remains the compatibility wrapper; new compact-table code can use `useMiniLogsTable` or `MiniLogsTable`. `useLogsPageTable` owns the full-page table (mount-time row snapshot, transaction appends from the mini feed, one importance-filter method); `PageLogs` / `logsApi.React.PageLogs` remain the thin compatibility wrappers over it.
510
+ Logger notification/tabs chrome uses `--logs-*` CSS variables; apps can theme it without forking logger components. `createLogsController` owns the headless append/limit/settings state for custom integrations. `useMessageEventLogsController` owns the global notification queue/timers/settings, `MessageEventLogsView` draws it, and `MessageEventLogs` / `logsApi.React.Message` remain compatibility wrappers. `MiniLogs` remains the compatibility wrapper; new compact-table code can use `useMiniLogsTable` or `MiniLogsTable`. `useLogsPageTable` owns the full-page table (mount-time row snapshot, transaction reconciliation to the controller's bounded state, one importance-filter method); `PageLogs` / `logsApi.React.PageLogs` remain the thin compatibility wrappers over it.
511
511
 
512
512
  ## Styles / Theme
513
513
  ```