wenay-react2 1.0.41 → 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 (58) hide show
  1. package/doc/EXAMPLE_USAGE.md +58 -2
  2. package/doc/PROJECT_FUNCTIONALITY.md +5 -1
  3. package/doc/changes/v1.0.41.md +23 -0
  4. package/doc/changes/v1.0.42.md +26 -0
  5. package/doc/progress/architecture-fix-queue.md +74 -0
  6. package/doc/progress/style-system-normalization.md +5 -5
  7. package/doc/target/my.md +39 -67
  8. package/doc/wenay-react2-rare.md +118 -13
  9. package/doc/wenay-react2.md +75 -11
  10. package/lib/common/api.d.ts +1 -0
  11. package/lib/common/api.js +8 -4
  12. package/lib/common/src/components/Dnd/DragArea.d.ts +5 -0
  13. package/lib/common/src/components/Dnd/DragArea.js +5 -1
  14. package/lib/common/src/components/Dnd/FloatingWindow.d.ts +9 -5
  15. package/lib/common/src/components/Dnd/FloatingWindow.js +39 -97
  16. package/lib/common/src/components/Dnd/Resizable.d.ts +3 -7
  17. package/lib/common/src/components/Dnd/Resizable.js +4 -3
  18. package/lib/common/src/components/Menu/RightMenu.js +8 -4
  19. package/lib/common/src/components/Menu/RightMenuStore.d.ts +2 -2
  20. package/lib/common/src/components/Menu/RightMenuStore.js +5 -3
  21. package/lib/common/src/components/Modal/LeftModal.js +4 -2
  22. package/lib/common/src/components/Modal/ModalContextProvider.js +5 -16
  23. package/lib/common/src/components/MyResizeObserver.d.ts +24 -0
  24. package/lib/common/src/components/MyResizeObserver.js +49 -0
  25. package/lib/common/src/components/Overlay.d.ts +24 -0
  26. package/lib/common/src/components/Overlay.js +28 -0
  27. package/lib/common/src/components/Settings/SettingsDialog.js +21 -22
  28. package/lib/common/src/components/Toolbar/Toolbar.d.ts +1 -0
  29. package/lib/common/src/components/Toolbar/Toolbar.js +16 -23
  30. package/lib/common/src/grid/columnState/ColumnsMenu.js +4 -13
  31. package/lib/common/src/grid/columnState/columnGrid.d.ts +103 -0
  32. package/lib/common/src/grid/columnState/columnGrid.js +231 -0
  33. package/lib/common/src/grid/columnState/columnState.js +10 -9
  34. package/lib/common/src/grid/columnState/index.js +3 -0
  35. package/lib/common/src/hooks/useDraggable.d.ts +8 -0
  36. package/lib/common/src/hooks/useDraggable.js +13 -4
  37. package/lib/common/src/hooks/useOutside.d.ts +4 -1
  38. package/lib/common/src/hooks/useOutside.js +2 -1
  39. package/lib/common/src/logs/logs.d.ts +96 -5
  40. package/lib/common/src/logs/logs.js +147 -108
  41. package/lib/common/src/logs/logsController.d.ts +3 -0
  42. package/lib/common/src/utils/cache.d.ts +12 -0
  43. package/lib/common/src/utils/cache.js +0 -0
  44. package/lib/common/src/utils/fixedOrder.d.ts +15 -0
  45. package/lib/common/src/utils/fixedOrder.js +30 -0
  46. package/lib/common/src/utils/index.d.ts +2 -0
  47. package/lib/common/src/utils/index.js +2 -0
  48. package/lib/common/src/utils/memoryStore.d.ts +3 -6
  49. package/lib/common/src/utils/memoryStore.js +1 -3
  50. package/lib/common/src/utils/persistedMaps.d.ts +16 -0
  51. package/lib/common/src/utils/persistedMaps.js +5 -0
  52. package/lib/common/src/utils/searchHistory.js +7 -6
  53. package/lib/common/src/utils/structEqual.d.ts +12 -0
  54. package/lib/common/src/utils/structEqual.js +40 -0
  55. package/lib/common/updateBy.d.ts +3 -0
  56. package/lib/common/updateBy.js +57 -22
  57. package/lib/style/style.css +58 -1
  58. package/package.json +2 -2
@@ -12,7 +12,7 @@ New code teaches and imports the short canonical surface:
12
12
 
13
13
  Old names are recorded only in `WENAY_REACT2_RENAMES.md`; do not export new compatibility aliases casually. Existing public APIs should usually remain supported during internal refactors; removals require an explicit migration cut with a separate task, migration notes, changelog, and usage signal. Default style contracts are stricter: do not remove old default classes/styles until the replacement class/token path is shipped and documented.
14
14
  Do not add new examples with Get*, FuncJSX, *2/*3, or business-specific helper names.
15
- If a primitive needs app policy, build a small app wrapper above it.
15
+ If a primitive needs app policy, build a small app wrapper above it.
16
16
  If behavior is reusable, expose it first as a headless use*/create* API. The hook/controller may return methods, getters, props/bind helpers, or a small API object; visual components and QA cards should consume that surface instead of becoming the only implementation.
17
17
  ```
18
18
 
@@ -53,6 +53,14 @@ confirmModal({modal, func, password?})
53
53
 
54
54
  `inputModal` and `confirmModal` accept either a setter function or the `useModal()` controller.
55
55
 
56
+ Internal Overlay (A9, 2026-07-10): `components/Overlay.tsx` is the ONE portal+scrim+outside-click+
57
+ Escape composition; `ModalProvider` and `SettingsDialog` are adapters over it (behavior 1:1,
58
+ SettingsDialog keeps its own two-stage Escape by NOT passing `onEscape`). It is deliberately not
59
+ exported: apps use ModalProvider/useModal/SettingsDialog. The render-slot stores above, the
60
+ LeftModal drawer (gesture-driven, scrim-less), and `ModalWrapper`/Input helpers (backdrop-less,
61
+ hosted inside another modal slot) are NOT overlays and stay separate. This leaf is also where all
62
+ scrim/portal DOM lives - the seam for a future react-native view layer.
63
+
56
64
  Left-side modal/menu helpers:
57
65
  ```
58
66
  LeftModal({arr, zIndex})
@@ -229,7 +237,7 @@ Semantics that are easy to get wrong:
229
237
  (setting columnDefs resets grid order). `setPresentGate(keys|null)` is for stable-schema app modes:
230
238
  it marks gated-out buttons disabled and hides those grid columns through applyColumnState without
231
239
  mutating persisted `visible`. No loop: applyColumnState never adds/removes columns. Null actual presence
232
- and null gate together mean all present; if a gate is set, grid-less consumers still see that gate.
240
+ and null gate together mean all present; if a gate is set, grid-less consumers still see that gate.
233
241
  - `listSource` = the `{order, visible}` slice exposed as a `UiListSource` (Toolbar's
234
242
  external-config contract): plug it into `createToolbar({source})` and the toolbar, the icon menu
235
243
  and the grid all mirror one config. Its setConfig MERGES visible and re-appends via normalize, so
@@ -244,6 +252,29 @@ Semantics that are easy to get wrong:
244
252
  stand: hot-reload recreates a module-level controller while the live grid stays attached to the
245
253
  OLD instance, so store->grid needs an F5; in a real app the module runs once.
246
254
 
255
+ High-level kit (`createColumnGrid`, `grid/columnState/columnGrid.tsx`):
256
+ ```
257
+ createColumnGrid({key, columnDefs?, columns?, data?, getId?, def?, saveMs?, toolbar?, autoSizeOnColumnCountChange?})
258
+ -> {state, toolbar, api, grid, tableProps, Table, Menu, Dots, Cards, Toolbar, Settings, View}
259
+ useColumnGrid(opts) -> same controller, captured once for component-local setups
260
+ ```
261
+ - This is the default convenience layer for ordinary app grids that all need the same column menu.
262
+ It infers `ColumnMeta` from leaf `ColDef`s (`colId` -> `field`, title from `headerName` -> key)
263
+ and merges optional `columns` overrides for `fixed`, `short`, `icon`, `defaultVisible`, `cardRole`,
264
+ and group semantics. Unknown explicit overrides are appended, so grid-less/card-only fields still work.
265
+ - `Table` and `tableProps()` wrap `AgGridTable` and wire `state.grid.attach/detach` automatically.
266
+ Their default `autoSizeColumns=false` is deliberate: the wrapper is for persisted column layout, so
267
+ auto-fit must be opt-in. `autoSizeOnColumnCountChange` is a narrower opt-in: it calls
268
+ `sizeColumnsToFit()` only when `visibleKeys().length` changes after a columnState edit.
269
+ - `View` is a small representation switch (`mode:'table'|'cards'`) with controls
270
+ (`'menu'|'dots'|'toolbar'|false|'auto'`). `auto` resolves to the built-in dots overlay for both
271
+ table and card mode; the wrapper sets dot max to the full column count unless overridden. `Dots` is
272
+ not card-specific; on table mode it edits the same config and the attached grid applies visibility,
273
+ order, and sort through columnState. `data`/`getId` can live on the controller defaults or be
274
+ passed per `View` render.
275
+ - Use raw `createColumnState` plus `createToolbar` when the app needs a custom skin like QA card 30,
276
+ a runtime `presentGate`, special group mode buttons, or nonstandard persistence timing.
277
+
247
278
  Icon menu (ColumnsMenu / MenuStrip, `grid/columnState/ColumnsMenu.tsx`):
248
279
  ```
249
280
  <MenuStrip items={MenuStripItem[]} onItem? onMove? move? tail? holdMs?=150 compact? />
@@ -284,7 +315,7 @@ Mobile (no ag-grid, no storage - the config alone):
284
315
  as dots move), `cardRole:'title'` = the header (else the first visible key), `cardRole:'accent'`
285
316
  = a badge, the rest are label+value. It sorts by the sticky `config.sort` itself
286
317
  (numeric/locale comparator) even when the sorted column is hidden.
287
- Styling uses shared classes `.wenayColDots*` / `.wenayCardList*`; runtime geometry remains inline because dot positions and drag transforms are state-derived. QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over columnState.listSource + pseudo-icons).
318
+ Styling uses shared classes `.wenayColDots*` / `.wenayCardList*`; runtime geometry remains inline because dot positions and drag transforms are state-derived. QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over columnState.listSource + pseudo-icons), 32 (createColumnGrid wrapper + dots driving table/cards).
288
319
 
289
320
  ## Outside / Buttons Compatibility
290
321
  ```
@@ -297,6 +328,10 @@ StyleOtherColumn
297
328
 
298
329
  `Button`, `OutsideButton`, `HoverButton`, and `AbsoluteButton` are still direct components rather than hook controllers.
299
330
 
331
+ `Button` persists its open/closed status under `keyForSave` (module-lifetime; same naming as
332
+ FloatingWindow/Resizable/RightMenu); the old `keySave` prop is a deprecated alias (`keyForSave`
333
+ wins when both are set).
334
+
300
335
  ## Drag / Resize Low Level
301
336
  ```
302
337
  FloatingWindowBase(props) // lower-level react-rnd wrapper
@@ -306,8 +341,8 @@ floatingWindowMap // persisted RND map (ObservableMap)
306
341
  FloatingWindowUpdate
307
342
  FloatingWindowProps / FloatingWindowController / FloatingWindowSavedGeometry
308
343
 
309
- DragBox(props) // absolute-position movement component
310
- DragArea(props) // low-level movement component
344
+ DragBox(props) // delta-drag component; thin adapter over useDraggableApi (A7)
345
+ DragArea(props) // @deprecated: unique semantics kept as-is; prefer useDraggableApi/DragBox
311
346
  FResizableReact(props)
312
347
  mapResiReact // persisted resize map (ObservableMap)
313
348
  OutlineDragDemo()
@@ -321,6 +356,18 @@ const drag = useDraggableApi(...)
321
356
 
322
357
  `useDraggable(...)` is kept as the simple hook wrapper; `useDraggableApi(...)` is the controller-style shape.
323
358
 
359
+ Drag primitives after the A7 pass (2026-07-10): `DragBox` is a thin adapter over
360
+ `useDraggableApi({holdMs: 0, trackState: false, onMove})` - same observable contract as its old
361
+ bespoke loop (immediate start, per-tick imperative `onX`/`onY` with the delta from the press
362
+ point, no re-render per move tick, `onStop` only after a real gesture, `last` ref shares the live
363
+ position object; pinned by `__test/dragBox.test.tsx`, QA card 35). `DragArea` stays untouched and
364
+ `@deprecated`: no consumers, and its semantics are unique (document.body listeners,
365
+ `stopPropagation` per move tick, ABSOLUTE coords, no preventDefault) - re-basing it would change
366
+ observable behavior; removal only in a breaking version. `useFloatingWindowController`'s header
367
+ loop and `StickerMenu` deliberately keep their own loops: viewport clamp + `e.buttons===1` release
368
+ recovery + persistence/z-stack, and mount-lifetime listeners + click-vs-drag suppression
369
+ respectively, are load-bearing behavior `useDraggableApi` does not model.
370
+
324
371
  Reorder-by-drag (useReorder, `hooks/useReorder.tsx` - extracted from the Toolbar editor, which is
325
372
  its first consumer):
326
373
  ```
@@ -519,6 +566,10 @@ Semantics that are easy to get wrong:
519
566
  - `useReplayHistory.seek` kills the live subscription imperatively (guarded off, double-call safe), folds `history.at(where)` through `apply` (keyframe + tail; a keyframe fully redefines consumer state, so `reset` is rarely needed), and freezes. `play()` resubscribes `{since: pos}` -> archive tail -> live handover.
520
567
  - `useStoreReplayEach` composes existing pieces instead of calling `Observe.syncStoreReplayEach` (which builds a FRESH store per call): the mirror lives in a ref (like `useStoreReplayMirror`), `store.each()` is subscribed in an effect declared BEFORE the sync effect (hook-call order guarantees the per-key subscriber exists when the keyframe applies — the expansion is never missed), then `useStoreReplaySync` drives the wire. Consequences: within one mount every resubscribe (StrictMode, `restart()`, `enabled` toggle, `staleMs`/`policy` change) reconnects by tail ON TOP of kept state and the consumer sees only the diff — no snapshot/`initial` dance (the library one-call needs `{since, initial: snapshot}` for that). After a FULL unmount pass `{since: prev.seq(), initial: prev.store.snapshot()}` like the library contract. `drain` is creation-time (it goes to `createStore`); changing it later does nothing. The fold target must live outside React state (ref/Map/grid api) — the hook renders nothing; `controller.store` is a normal store for `useStoreNode`/`useStoreKeys` extras.
521
568
  - Server primitives (`conflateReplay` per connection, `archiveReplay`) intentionally have no hooks: they live where the RPC server is built.
569
+ - Route coordinator (common2 1.0.67 `Replay.createRouteCoordinator`): the coordinator owns promotion/fallback/shadow via policy hooks; consumers subscribe on a pair `link`, whose handle matches our route hooks (`ready, seq(), label(), active()`) minus `switchRoute` — the transport switch is invisible by the seq contract. Our route hooks (`useReplayRouteSubscribe` etc.) remain the manual-`switchRoute` wrapper over `replayRouteSubscribe`; they do NOT sit on top of a coordinator. React surface for coordinator links (subscribe hook + route-state/`promoteDirect` controls for ops UI) is a target-backlog item — do not bend the existing route hooks to accept links implicitly.
570
+ - WebRTC direct path (common2 1.0.68): `createWebRtcConnector({port, rtc, ...})` is the coordinator's direct `RouteConnector`; `rtc` is an injected factory and in the browser that injection (`() => new RTCPeerConnection(cfg)`) belongs to the app/React side — common2 stays lib.dom-free. Signaling rides the EXISTING rpc socket (`createSignalHub` port exposed by `createRpcServerAuto`), so no new connection management appears in React; channel death / revoke surfaces as loud `onError` on the line (never silence), which the existing hooks already map to `error`. Nothing here needs a hook by itself — it matters as the direct transport under a future adapter (target backlog).
571
+ - Peer SDK (common2 1.0.69 `Peer` / `wenay-common2/peer`): the one-call layer over rpc + store + replay + route coordinator — `createPeerClient({remote, account, initial, rtc?})` publishes the own store as a patch line and `peer(account)` returns a live mirrored store + route control; relay and direct share the OWNER's seq space (`createPatchRelayJournal`), so hand-offs are plain seq resumes, no keyframe reset. This is the intended React adoption surface (a `usePeer` adapter is the target-backlog item, superseding the raw coordinator-link hook idea): the mirrored store already plugs into `useStoreNode`/`useStoreKeys` as-is. 1.0.70 ships the oracle suites + `demo/` stand in the package — living examples per subsystem (`replay/peer-sdk.test.ts` is the Peer contract).
572
+ - Media capture (common2 1.0.66 `Media` namespace / `wenay-common2/media`): CONSUMPTION needs no new hooks — `Media.createAudioSource` / `Media.createVideoSource` return `[emit, listen] & control`, with `replay:true` the `listen` is a normal replay line, so `useReplaySubscribe` / `useReplayFrame` cover it; frames are one `Uint8Array` (40-byte fixed header + raw payload, `Media.decodeMediaFrame`) and follow the existing fold-outside-React rule (canvas/AudioContext via ref). The capture `control` side (`start()` resolving to `'idle'|'requesting'|'live'|'denied'|'no-device'|'error'`, `stop()`, `setDevice`, `listDevices`, stats) is a permission/device/lifecycle state machine — exactly hook-shaped; a `useMediaSource` capture hook + QA card is a recorded target goal (supersedes the earlier "wrap it app-locally" stance). Backpressure defaults live in common2 (audio = lossless queue, video `replay:true` = keep-latest frame recovery); the client-side knobs are the same `policy`/`hint` as any replay line.
522
573
 
523
574
  QA cards 23/24/25/26 (`testUseReact/replayVideo.tsx`, all in-proc): synthetic 10fps jpeg-frame producer on `Replay.replayListen({history, current})`; client A = direct `exposeReplay` remote; client B = simulated slow wire (1 envelope per rateMs) behind `conflateReplay({pending: () => buf.length, highWater: 4, lowWater: 1, keyOf: () => "frame"})`; client C = `archiveReplay` + `openHistory` scrubber; client D = freshness (`staleMs: 2000`, `React.memo` + no tick, mounted inside a local `<StrictMode>`; the flat renders counter under growing frames is the no-per-event-render proof; "stall producer" toggles the emit interval, "new client" remounts by key for the stalled-mount case; card 24 has the same via `staleMs: 2500` on the mirror); client E = pull path (`useReplayFrame` over the direct remote with a wrapped counting `frame()`, pace switch 250ms/1s/3s keeps seq). `window.__replayVideoDemo` is exposed for debugging (wire.setRateMs, stats). Node-verified: slow wire delivered 12/36 envelopes yet converged to the last frame with bounded buffer (coalesced tail recovery); `syncStoreReplay` off() freezes the mirror and `{since}` resubscribe catches up by tail. Card 25 = per-key feed (`useStoreReplayEach` over `exposeStoreReplay`): a dict-of-rows store, producer touches ONE random row per tick, the fold target is a plain Map with per-row cb counters — only the mutated row's counter grows, keyframe/`replace` are the only whole-table expansions, delete arrives as `(key, undefined)`. Card 26 = route hand-off (`useReplayRouteSubscribe` over the same video line): one canvas starts on relay, switches direct/relay by `switchRoute`, and failed replacement keeps the previous route alive. Browser QA of throttling-sensitive behavior needs a VISIBLE tab: hidden-tab timer/effect throttling stalls the producer and delays passive effects (known stand caveat).
524
575
 
@@ -555,6 +606,18 @@ MainPage()
555
606
 
556
607
  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.
557
608
 
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
614
+ (`applyImportanceFilter(min?)`: set -> `setFilterModel` greaterThanOrEqual on `var`, unset ->
615
+ `destroyFilter`), used by both the settings subscription and `onGridReady` (which skips the
616
+ destroy branch - a fresh grid has no filter). Both data subscriptions are `updateBy(obj, cb)`
617
+ 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
619
+ `update` prop, which `PageLogs` still honors. QA card 9.
620
+
558
621
  ## Cache / Memory / Browser Utilities
559
622
  ```
560
623
  browserCacheStorage
@@ -577,6 +640,9 @@ memoryUpdate(key, mutate)
577
640
  memoryMarkDirty(key)
578
641
  createSearchHistory({key, max?})
579
642
  deepMergeWithMap(target, source)
643
+ structEqual(a, b) // deep equality for plain data trees; JSON.stringify-idiom
644
+ // tolerances (undefined props absent, NaN==NaN) WITHOUT
645
+ // its key-order sensitivity; not for Maps/Sets/cycles
580
646
  memoryCache
581
647
  memoryMaps
582
648
 
@@ -584,9 +650,27 @@ ArrayPromise({arr, catchF?, thenF?})
584
650
  PageVisibilityProvider
585
651
  PageVisibilityContext
586
652
  setAutoStepForElement(input, {minStep?, maxStep?})
653
+
654
+ useCacheMapPersistence(cache: CacheMap, delay?=300) -> {isDirty(), flush(), save(), reload()}
587
655
  ```
588
656
 
589
- Cache helpers are process/browser storage utilities, not React state management.
657
+ Layering (2026-07 architecture pass): the persisted maps (`floatingWindowMap`, `mapResiReact`,
658
+ `mapRightMenu`) are DECLARED in the utils leaf `utils/persistedMaps.ts` and re-exported by their
659
+ owning components (FloatingWindow/Resizable/RightMenuStore) - importing `memoryStore`/`memoryCache`
660
+ no longer pulls react-rnd or the Menu tree; only `import type` lines point upward (erased at build).
661
+ Shared order helper `utils/fixedOrder.ts` (`pinFixedOrder`, `movedOrderWithFixed`) is the single
662
+ implementation of the "fixed entries pin to descriptor index" invariant used by columnState,
663
+ ColumnsMenu, and Toolbar (it used to be four inline copies that had to stay byte-identical).
664
+
665
+ Cache helpers are process/browser storage utilities, not React state management. The one React
666
+ entry here is `useCacheMapPersistence` - the documented app contract (`load()` on mount +
667
+ `onDirty -> saveDebounced(delay)`) as a hook. Scope is deliberately exactly that: the
668
+ pagehide/visibilitychange flush backstops stay app-side (they are page-level policy, not
669
+ per-component). Effect identity is `[cache, delay]`; the returned methods are pass-throughs and
670
+ `reload()` is an alias of `load()` - a repeated load MERGES storage on top of the current maps
671
+ (addDataToMap semantics), it is not a reset. QA cards 20/21/25 share one `memoryCache`, so
672
+ several mounted hooks coexist: load() is idempotent-enough (merge) and extra onDirty
673
+ subscribers just debounce the same save.
590
674
 
591
675
  Dirty/save contract: the dirty signal originates in the data layer. The persisted maps are
592
676
  `ObservableMap`s, so `set/delete/clear` announce themselves; in-place mutations of stored
@@ -612,9 +696,20 @@ CResizeObserver
612
696
  setResizeableElement(el)
613
697
  removeResizeableElement(el)
614
698
  ObserveID
699
+
700
+ useResizeObserver<T>(onResize) -> {ref, element()}
701
+ useElementSize<T>() -> {ref, element(), width, height, getSize()}
615
702
  ```
616
703
 
617
- Use these only when a component must participate in the shared resize observer map.
704
+ Use the class/imperative pair only when a component must participate in the shared resize
705
+ observer map. The hooks are the React-shaped entry over the SAME module singleton (one native
706
+ `ResizeObserver` per app): `useResizeObserver` returns a callback ref (attach/detach re-observes;
707
+ React null-call on unmount unsubscribes) and takes `onResize` through a ref - new identity
708
+ neither resubscribes nor is missed. The native observer fires once right after `observe`, so the
709
+ first measurement arrives without an explicit initial read. `useElementSize` folds that into
710
+ rounded, equality-guarded `{width, height}` state (a no-op resize does not re-render) plus a live
711
+ `getSize()` getter (exact floats, no render wait). `setResizeableElement`'s auto-shrink path is
712
+ untouched and still class-driven (ParamsEditor inputs). QA card 19 drives both hooks.
618
713
 
619
714
  ## Styles
620
715
  ```
@@ -640,9 +735,9 @@ Current tokenized prefixes:
640
735
  - `--wnd-*` for `FloatingWindow` chrome.
641
736
  - `--dlg-*` for `SettingsDialog` chrome.
642
737
  - `--tb-*` for toolbar chrome.
643
- - `--logs-*` for global/context logger chrome.
644
- - `--cols-menu-*` for compact `ColumnsMenu/MenuStrip` chrome.
645
- - `--cols-dots-*` for `ColumnDots` chrome.
738
+ - `--logs-*` for global/context logger chrome.
739
+ - `--cols-menu-*` for compact `ColumnsMenu/MenuStrip` chrome.
740
+ - `--cols-dots-*` for `ColumnDots` chrome.
646
741
  - `--cols-card-*` for `CardList` chrome.
647
742
  - `--wenay-z-modal` for modal/overlay stacking.
648
743
 
@@ -650,11 +745,11 @@ Normalization rule: new shared CSS should first try an existing token. Add a new
650
745
 
651
746
  Open normalization candidates:
652
747
  - `src/style/style.css`: `.msTradeAlt`, `.msTradeActive`, `.newButtonSimple`, `.toIndicatorMenuButton:hover`, submit-button green, and several toolbar row hover/drag literals still use raw colors.
653
- - `src/common/src/grid/columnState/*`: compact menu/dots/card visuals now use `.wenayColsMenu*`, `.wenayColDots*`, `.wenayCardList*` plus `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`; further changes here should be visual QA only, not a new default palette.
748
+ - `src/common/src/grid/columnState/*`: compact menu/dots/card visuals now use `.wenayColumnGrid*`, `.wenayColsMenu*`, `.wenayColDots*`, `.wenayCardList*` plus `--cols-grid-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`; further changes here should be visual QA only, not a new default palette.
654
749
  - `src/common/src/components/ParamsEditor.tsx` and `src/common/src/components/Input.tsx`: if these stay public primitives, define default class/token contracts instead of component-owned visual styling.
655
750
  - `src/common/src/styles/commentaryStyles.css`: standalone `.commentary` CSS is not imported by the root style bundle; either import/tokenize it if still used, or mark it as a local component concern.
656
751
 
657
- Recently normalized: mouse context-menu item colors through `--menu-*`, `--menu-outline-color` for `OutlineDragDemo`, `--logs-*` for logger chrome, `--dlg-scrim` in `ModalProvider`, compact `ColumnsMenu/MenuStrip` visuals through `.wenayColsMenu*` / `--cols-menu-*`, and card-29 mobile primitives through `--cols-dots-*` / `--cols-card-*`.
752
+ Recently normalized: mouse context-menu item colors through `--menu-*`, `--menu-outline-color` for `OutlineDragDemo`, `--logs-*` for logger chrome, `--dlg-scrim` in `ModalProvider`, compact `ColumnsMenu/MenuStrip` visuals through `.wenayColsMenu*` / `--cols-menu-*`, card-29 mobile primitives through `--cols-dots-*` / `--cols-card-*`, and createColumnGrid overlay through `.wenayColumnGrid*` / `--cols-grid-*`.
658
753
 
659
754
  ## Cleanup Inventory
660
755
 
@@ -668,6 +763,16 @@ Suspicious but still public:
668
763
  - `src/common/src/myChart/chartEngine/chartEngine.ts` is a reference copy next to the public React engine. It is not exported from root; treat it as suspicious maintenance debt, not as public API.
669
764
  - `StyleCSSHeadGridEdit` and `StyleCSSHeadGrid` mutate `<head>` directly. They are exported and can have consumers, but new grid styling should prefer ag-grid theme params and tokens.
670
765
 
766
+ Added by the 2026-07-09 architecture audit (evidence in the audit report / `doc/target/my.md`):
767
+ - `menuR.tsx` (`createRightClickMenu` / `MenuR`) - CONFIRMED dead: no runtime consumer anywhere (the test that supposedly covers it imports `RightMenu.tsx`); re-exported twice from `api.tsx` (`export *` + `kit.menu.rightClick`). Candidate for removal in a deliberate breaking version; its gesture code duplicates `menuMouse.tsx:342-380`.
768
+ - `logsContext.tsx` - a full PARALLEL logs stack (own `LogEntry` shape divergent from `logsController`, raw unguarded `localStorage`, own settings/notifications/table) exported from the barrel next to `logs.tsx`. Decide: deprecate or converge; new code should use `logsApi`/`createLogsController`.
769
+ - `MyChartEngine` - a hardcoded DEMO (random data + setInterval inside `useEffect`, no props but `style`) exported as public API from `chartEngineReact.tsx`; `generateIncrementalData` (Math.random demo util) is public too. Should move to demo namespace or gain a real props contract.
770
+ - ~~`getLogsApi<T>()` shared module state~~ FIXED (A2, v1.0.41): every call now builds its own map/mini/settings (optional `settingsKey` persists per instance); the global `logsApi` explicitly injects the legacy shared state.
771
+ - ~~`FloatingWindow` `onCLickClose` typo~~ ALIASED (A10, v1.0.42): `onClickClose` added, typo prop is a deprecated alias; REMOVAL of the alias still needs a breaking pass.
772
+ - `DragArea` - `@deprecated` (A7, v1.0.42): no consumers; unique semantics kept as-is (see Drag / Resize Low Level). Removal in a deliberate breaking version.
773
+ - `Button` `keySave` prop - deprecated alias of `keyForSave` (A10, v1.0.42); removal in a breaking pass.
774
+ - `contextMenu` raw `map`/legacy-queue path is test-only-live (populated only by `contextMenuStats.test.tsx`); the `bb`/`map.clear()` reopening invariant itself is intentional and stays.
775
+
671
776
  ## Charts
672
777
  Canvas chart:
673
778
  ```
@@ -699,4 +804,4 @@ removed old grid update names -> applyGridRows / agGrid4
699
804
  ```
700
805
 
701
806
  Do not add new generic utilities with app words in their signature. For example, a column primitive should accept
702
- `names` and `apply`, while a product wrapper decides group ids, column ids, and labels.
807
+ `names` and `apply`, while a product wrapper decides group ids, column ids, and labels.
@@ -21,9 +21,11 @@ Put app-specific layout/build rules in an app wrapper above the primitive.
21
21
  ## Render Memory
22
22
  ```
23
23
  updateBy(obj) / useUpdateBy(obj) // subscribe current component to renderBy(obj)
24
+ updateBy(obj, cb) // imperative callback instead of a re-render;
25
+ // cb goes through a ref - inline identity is fine
24
26
  renderBy(obj, ms?) // emit render for subscribers
25
- renderByRevers(obj, ms?, reverse?=true)
26
- renderByLast(obj, ms?)
27
+ renderByRevers(obj, ms?, reverse?=true) // reverse/last affect ONLY React subscribers;
28
+ renderByLast(obj, ms?) // updateBy(obj, cb) callbacks always all run first
27
29
 
28
30
  const state = { count: 0 }
29
31
  const api = createUpdateApi(state)
@@ -63,6 +65,13 @@ document.addEventListener("visibilitychange",
63
65
  window.addEventListener("pagehide", () => { void memoryCache.flush() }) // backstop: flush is async
64
66
  ```
65
67
 
68
+ The load + dirty->saveDebounced part of that contract as one hook (React components):
69
+ ```
70
+ const persistence = useCacheMapPersistence(memoryCache, delayMs?=300)
71
+ persistence.isDirty(); persistence.flush(); persistence.save(); persistence.reload() // reload = load() alias
72
+ ```
73
+ The pagehide/visibility flush backstops stay app-side (see above) - the hook deliberately does not own them.
74
+
66
75
  ## Outside Click / Buttons
67
76
  ```
68
77
  const outside = useOutside({onOutside, enabled?})
@@ -73,19 +82,35 @@ outside.contains(event.target)
73
82
 
74
83
  <OutsideClickArea outsideClick={close} status={open}>...</OutsideClickArea>
75
84
 
76
- <Button button={<button>Open</button>} outClick>{...}</Button>
85
+ <Button button={<button>Open</button>} outClick keyForSave?>{...}</Button> // keySave = deprecated alias
77
86
  <OutsideButton button={...}>{...}</OutsideButton>
78
87
  <HoverButton button={...}>{...}</HoverButton>
79
88
  <AbsoluteButton button={...}>{...}</AbsoluteButton>
80
89
  ```
81
90
 
91
+ ## Element Size / Resize Observer
92
+ ```
93
+ const box = useResizeObserver<HTMLDivElement>(() => onResize()) // shared singleton observer
94
+ <div ref={box.ref} />; box.element()
95
+
96
+ const size = useElementSize<HTMLDivElement>() // "want the size -> get the value/method"
97
+ <div ref={size.ref} />
98
+ size.width; size.height // state, rounded, no-op resize does not re-render
99
+ size.getSize() // live getter (exact, no render wait)
100
+
101
+ setResizeableElement(el) / removeResizeableElement(el) // legacy imperative auto-shrink path, unchanged
102
+ ```
103
+ Both hooks ride the same module-level `CResizeObserver` singleton (one native `ResizeObserver` for the whole app). QA card 19.
104
+
82
105
  ## Drag / Floating Windows
83
106
  ```
84
- const drag = useDraggableApi({initialPosition, holdMs?, onDragStart?, onDragEnd?})
107
+ const drag = useDraggableApi({initialPosition, holdMs?, onDragStart?, onDragEnd?, onMove?, trackState?})
85
108
  <div {...drag.bind} style={{transform: `translate(${drag.position.x}px, ${drag.position.y}px)`}} />
86
109
  drag.setPosition({x, y})
87
110
  drag.resetPosition()
88
111
  drag.cancelDrag()
112
+ // imperative path: onMove(p) fires per move tick (through a ref); trackState:false stops
113
+ // per-tick re-renders - position lives only in drag.positionRef/onMove (DragBox is this shape)
89
114
 
90
115
  const r = useReorder({order, commit, move?, canDrag?, preview?, holdMs?}) // mini reorder-by-drag
91
116
  <div ref={r.listRef}>{order.map(k => { // children 1:1 with order
@@ -99,7 +124,8 @@ const b = useReorderBoard({columns: [{key, items}], commit, // d
99
124
  b.over // {col, index} | null - live target
100
125
  // column gravity is YOUR CSS: justify-content flex-start packs up, flex-end packs down
101
126
 
102
- <FloatingWindow keyForSave="tool" size={{width: 320, height: 240}} header={<div>Tool</div>}>
127
+ <FloatingWindow keyForSave="tool" size={{width: 320, height: 240}} header={<div>Tool</div>}
128
+ onClickClose={() => setOpen(false)}> {/* onCLickClose (typo) = deprecated alias */}
103
129
  <Panel />
104
130
  </FloatingWindow>
105
131
 
@@ -156,6 +182,7 @@ tb.api.useConfig() / getConfig() / setConfig(next) / setOrder(order) / show(key,
156
182
  tb.api.showSettings(on) / showReset(on) // pseudo-controls visibility
157
183
  tb.api.useItems() // headless bar: ordered visible [{item, density, content}] - custom markup
158
184
  tb.api.onChange.on(cfg => ...) -> off // fires on every edit
185
+ tb.api.dispose() // release the external-source subscription (HMR/remounts); persisted config stays
159
186
 
160
187
  registerToolbarDensity({key, name, renderItem?}) -> unregister // built-ins: 'icon', 'label'
161
188
  toolbarItemIcon(item) // the item's icon, or its first letters as a text pseudo-icon
@@ -282,7 +309,7 @@ cs.api.show(key, on) / move(order) / setSort({key, dir} | null) / toggleSort(key
282
309
  cs.api.visibleKeys() // keys to render, in order (group-gated)
283
310
  cs.api.onChange.on(cfg => ...) -> off
284
311
  cs.api.usePresent() / isPresent(key) / setPresent(keys | null) // live-grid presence
285
- cs.api.getPresentGate() / setPresentGate(keys | null) // app runtime availability gate, not persisted
312
+ cs.api.getPresentGate() / setPresentGate(keys | null) // app runtime availability gate, not persisted
286
313
  cs.api.listSource // {order, visible} slice as a Toolbar `source`
287
314
  ```
288
315
  Config `{v, order, visible, width, sort, filter, groups}` is serializable and persisted like
@@ -317,7 +344,34 @@ Mobile (no ag-grid): dots selector + card rows over the same config:
317
344
  // drag=replace, swipe up=remove, tap dot=select, sort button cycles
318
345
  <CardList<Row> state={cs} data={rows} getId? renderValue? /> // visible cols -> card fields; cardRole:'title'/'accent'
319
346
  ```
320
- QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over the same config).
347
+
348
+ Default wrapper for repeated grid/menu/mobile use:
349
+ ```
350
+ const cg = createColumnGrid<Row>({
351
+ key: "orders.columns",
352
+ columnDefs: cols, // ColumnMeta inferred from colId/field/headerName
353
+ data: rows, // optional default; View props can override
354
+ getId: r => r.id,
355
+ autoSizeOnColumnCountChange: true, // optional fit when visible column count changes
356
+ columns: [{key: "name", fixed: true, cardRole: "title"}], // optional overrides
357
+ })
358
+
359
+ <cg.View mode="table" /> // default controls="auto" -> dots overlay
360
+ <cg.View mode="cards" data={otherRows} />
361
+ <cg.Table rowData={rows} getRowId={p => p.data.id} />
362
+ <cg.Menu compact />
363
+ <cg.Dots /> // no wrapper max; defaults to all columns
364
+ <cg.Toolbar settings />
365
+ <cg.Settings />
366
+ ```
367
+ `createColumnGrid` returns `{state, toolbar, api, grid, tableProps, Table, Menu, Dots, Cards, Toolbar, Settings, View, dispose}`; `dispose()` releases factory-lifetime subscriptions (config onChange, toolbar source, pending fit) without touching the persisted config. The columnState BARREL stays ag-grid-free; `createColumnGrid` ships from its own module (both are exported from the package root).
368
+ Its `Table`/`tableProps()` attach/detach the columnState grid adapter automatically and default
369
+ `autoSizeColumns=false` so restored widths are not overwritten. `autoSizeOnColumnCountChange` is
370
+ separate and only calls `sizeColumnsToFit()` when the visible column count changes. `useColumnGrid(opts)`
371
+ is the same controller captured once for component-local setups; keep module-level `createColumnGrid`
372
+ when the controller must survive route/component remounts.
373
+
374
+ QA cards 28 (grid layer + F5 restore), 29 (mobile dots + cards), 30 (toolbar icon menu + grouped sub-column mode button), 31 (Toolbar over the same config), 32 (createColumnGrid wrapper + dots driving table/cards).
321
375
 
322
376
 
323
377
  ## Observe React Adapter
@@ -416,6 +470,10 @@ const tt = useReplayHistory(history, (frame) => draw(frame), {head: () => replay
416
470
  tt.live; tt.seq; tt.head; tt.pause(); tt.play(); tt.seek({seq})
417
471
  ```
418
472
 
473
+ Route hand-off here is the MANUAL surface (`switchRoute`). common2 1.0.67 `Replay.createRouteCoordinator` moves route decisions (policy, promote/fallback/shadow) out of the consumer; a coordinator `link.subscribe(cb)` handle has the same shape (`ready, seq(), label(), active()`) and survives every route change, so components consuming a coordinator link do not need `switchRoute` at all. common2 1.0.68 supplies the direct transport for it: `createWebRtcConnector` with an app-injected `rtc: () => new RTCPeerConnection(cfg)` factory (the browser — i.e. the React app — owns that injection) and signaling over the existing RPC socket (`createSignalHub`). common2 1.0.69 wraps the whole stack into the `Peer` SDK (`wenay-common2/peer`): `createPeerClient(...).peer(account)` returns a live mirrored store (works with `useStoreNode`/`useStoreKeys` as-is) + route control, surviving relay<->direct hand-offs in one seq space. A dedicated `usePeer` adapter is a target-backlog item, not yet a library surface.
474
+
475
+ 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. 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.
476
+
419
477
  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.
420
478
 
421
479
  ## Logs
@@ -425,7 +483,9 @@ logsApi.addLogs({id: "api", time: new Date(), txt: "done", var: 1})
425
483
  <logsApi.React.PageLogs />
426
484
  <logsApi.React.Setting />
427
485
 
428
- const customLogs = getLogsApi<MyFields>({limitPer: 500, limit?: 50, varMin?: 0})
486
+ const customLogs = getLogsApi<MyFields>({limitPer: 500, limit?: 50, varMin?: 0, settingsKey?: "myLogs"})
487
+ // each getLogsApi call now owns ISOLATED full/mini/settings state (settingsKey persists the
488
+ // instance settings; omitted = fresh non-persisted). The global logsApi keeps the legacy shared state.
429
489
  const headless = createLogsController<MyFields>({options: {limitPer: 500, limit: 50}})
430
490
 
431
491
  const messageNotifications = useMessageEventLogsController({maxVisible: 4})
@@ -437,9 +497,13 @@ const contextNotifications = useLogsNotificationsController()
437
497
  const mini = useMiniLogsTable({data: rows, onClick: e => console.log(e.data)})
438
498
  <AgGridTable {...mini.props} />
439
499
  <MiniLogsTable data={rows} />
500
+
501
+ const table = useLogsPageTable() // full-page logs grid controller
502
+ <AgGridTable {...table.gridProps} />
503
+ table.fit(); table.applyImportanceFilter(min?); table.appendRow(row); table.getApi()
440
504
  ```
441
505
 
442
- 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`.
506
+ 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.
443
507
 
444
508
  ## Styles / Theme
445
509
  ```
@@ -455,7 +519,7 @@ useAgGridTheme("dark" | "light")
455
519
  colDefCentered / colDefWrap / numericComparator
456
520
  ```
457
521
 
458
- Shared CSS variables include `--menu-outline-color`, `--logs-*`, `--dlg-*`, `--wnd-*`, `--tb-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
522
+ Shared CSS variables include `--menu-outline-color`, `--logs-*`, `--dlg-*`, `--wnd-*`, `--tb-*`, `--cols-grid-*`, `--cols-menu-*`, `--cols-dots-*`, and `--cols-card-*`.
459
523
 
460
524
  ## Charts
461
525
  ```
@@ -472,4 +536,4 @@ npm run testReact // http://localhost:3010/
472
536
 
473
537
  The stand lives in `src/common/testUseReact/qa.tsx`. Use it for visual checks; agGrid4 overlay/dynamic-column demos are dedicated QA cards.
474
538
  Two tabs: Active checks (current work) and Verified archive (`#archive`) - verified/fixed cards move
475
- there and stay runnable for regression re-checks.
539
+ there and stay runnable for regression re-checks.
@@ -18,6 +18,7 @@ export * from "./src/components/Dnd";
18
18
  export * from "./src/utils";
19
19
  export * from "./src/grid/agGrid4";
20
20
  export * from "./src/grid/columnState";
21
+ export * from "./src/grid/columnState/columnGrid";
21
22
  export * from "./src/components/Buttons";
22
23
  export * from "./src/components/MyResizeObserver";
23
24
  export * from "./src/components/Other";
package/lib/common/api.js CHANGED
@@ -15,17 +15,21 @@ export * from "./src/styles/styleGrid";
15
15
  export * from "./src/styles/tokens";
16
16
  // 1. BASE LAYER - no internal project dependencies
17
17
  export * from "./updateBy";
18
- // 2. HOOKS - depend only on updateBy
18
+ // 2. HOOKS - depend on updateBy and wenay-common2
19
19
  export * from "./src/hooks";
20
- // 4. DND components - depend on updateBy
20
+ // 4. DND components - depend on updateBy and utils (persistedMaps)
21
21
  export * from "./src/components/Dnd";
22
- // 5. UTILS - now depend on Dnd (memoryStore imports floatingWindowMap, mapResiReact)
22
+ // 5. UTILS - self-contained (persisted maps live in utils/persistedMaps; components import
23
+ // their map from there, so utils no longer reaches into the component layer)
23
24
  export * from "./src/utils";
24
25
  // 5b. GRID (agGrid4): core buffer + headless hook + AgGridTable.
25
26
  export * from "./src/grid/agGrid4";
26
27
  // 5c. GRID column state (createColumnState): persisted order/visibility/width/sort/filter,
27
- // standalone config store + optional two-way ag-grid adapter (attach via onGridReady)
28
+ // standalone config store + optional two-way ag-grid adapter (attach via onGridReady).
29
+ // The columnState barrel is deliberately ag-grid-free; createColumnGrid (ag-grid runtime +
30
+ // Toolbar) ships from its own module so grid-less consumers can import the barrel directly.
28
31
  export * from "./src/grid/columnState";
32
+ export * from "./src/grid/columnState/columnGrid";
29
33
  // 6. BASE COMPONENTS - depend on hooks
30
34
  export * from "./src/components/Buttons";
31
35
  export * from "./src/components/MyResizeObserver";
@@ -1,3 +1,8 @@
1
+ /** @deprecated No consumers in the library; prefer `useDraggableApi` (delta-based, mouse+touch)
2
+ * or `DragBox` for the imperative onX/onY shape. Kept as-is because its semantics are unique
3
+ * (document.body listeners, stopPropagation on every move tick, ABSOLUTE x/y coordinates,
4
+ * no preventDefault) - re-basing it on useDraggableApi would change observable behavior.
5
+ * Candidate for removal in a deliberate breaking version (A7/A3 cleanup inventory). */
1
6
  export declare function DragArea({ children, onY, onX, x, y, last, onStart, onStop, }: {
2
7
  children: React.JSX.Element;
3
8
  onX?: (x: number) => void;
@@ -1,6 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
3
- //
3
+ /** @deprecated No consumers in the library; prefer `useDraggableApi` (delta-based, mouse+touch)
4
+ * or `DragBox` for the imperative onX/onY shape. Kept as-is because its semantics are unique
5
+ * (document.body listeners, stopPropagation on every move tick, ABSOLUTE x/y coordinates,
6
+ * no preventDefault) - re-basing it on useDraggableApi would change observable behavior.
7
+ * Candidate for removal in a deliberate breaking version (A7/A3 cleanup inventory). */
4
8
  export function DragArea({ children, onY, onX, x = 0, y = 0, last, onStart, onStop, }) {
5
9
  const lastC = useRef(null);
6
10
  const lastT = useRef(null);
@@ -1,6 +1,6 @@
1
1
  import React, { ReactNode } from "react";
2
2
  import { type RndResizeCallback } from "react-rnd";
3
- import { ObservableMap } from "../../utils/observableMap";
3
+ import { floatingWindowMap } from "../../utils/persistedMaps";
4
4
  export type FloatingWindowPosition = {
5
5
  x: number;
6
6
  y: number;
@@ -31,6 +31,9 @@ export type FloatingWindowProps = {
31
31
  position?: FloatingWindowPosition;
32
32
  size?: FloatingWindowSize;
33
33
  moveOnlyHeader?: boolean;
34
+ /** Show the close button and handle its click. */
35
+ onClickClose?: () => void;
36
+ /** @deprecated typo alias of {@link onClickClose}; kept for compatibility, `onClickClose` wins when both are set. */
34
37
  onCLickClose?: () => void;
35
38
  header?: React.ReactElement | boolean;
36
39
  overflow?: boolean;
@@ -48,7 +51,7 @@ export type FloatingWindowProps = {
48
51
  children: React.ReactElement | ((update: number) => React.ReactElement);
49
52
  className?: string;
50
53
  };
51
- export type FloatingWindowControllerOptions = Omit<FloatingWindowProps, "children" | "className" | "header" | "moveOnlyHeader" | "overflow" | "onCLickClose">;
54
+ export type FloatingWindowControllerOptions = Omit<FloatingWindowProps, "children" | "className" | "header" | "moveOnlyHeader" | "overflow" | "onCLickClose" | "onClickClose">;
52
55
  export type FloatingWindowController = {
53
56
  position: FloatingWindowPosition;
54
57
  size: FloatingWindowSize;
@@ -64,14 +67,14 @@ export type FloatingWindowController = {
64
67
  onResize: RndResizeCallback;
65
68
  onResizeStop: RndResizeCallback;
66
69
  };
67
- export declare const floatingWindowMap: ObservableMap<string, FloatingWindowSavedGeometry>;
70
+ export { floatingWindowMap };
68
71
  export declare const FloatingWindow: typeof FloatingWindowBase;
69
72
  export declare function useFloatingWindowController({ keyForSave: ks, position, size, zIndex, onUpdate, limit, sizeByWindow }?: FloatingWindowControllerOptions): FloatingWindowController;
70
73
  /**
71
74
  * Wrapper component around react-rnd.
72
75
  * Provides dragging and resizing, an optional header, and a close button.
73
76
  */
74
- export declare function FloatingWindowBase({ children, keyForSave: ks, position, size, overflow, zIndex, onUpdate, disableDragging, className, header, moveOnlyHeader, limit, onCLickClose, sizeByWindow }: FloatingWindowProps): import("react/jsx-runtime").JSX.Element;
77
+ export declare function FloatingWindowBase({ children, keyForSave: ks, position, size, overflow, zIndex, onUpdate, disableDragging, className, header, moveOnlyHeader, limit, onCLickClose, onClickClose, sizeByWindow }: FloatingWindowProps): import("react/jsx-runtime").JSX.Element;
75
78
  export type DragBoxProps = {
76
79
  /** Child element that should be draggable */
77
80
  children: ReactNode;
@@ -106,4 +109,5 @@ export type DragBoxProps = {
106
109
  * Function only as a hook for parameter changes during movement, although it has its own component (for offset counting).
107
110
  * Returns the distance traveled when moving the child element.
108
111
  */
109
- export declare function DragBox({ children, onX, onY, x, y, right, last, dragging, onStart, onStop }: DragBoxProps): import("react/jsx-runtime").JSX.Element;
112
+ export declare function DragBox({ children, onX, onY, x, y, right, last, dragging: _dragging, // accepted for compatibility, was never read
113
+ onStart, onStop }: DragBoxProps): import("react/jsx-runtime").JSX.Element;