The audit's directed verification confirmed the gap: panel sizes were
internal-only state (per-panel defaultSize seed + onResize/onResizeEnd
callbacks), so a consumer could not control or persist the layout. Add the
framework-norm bindable pair:
- `sizes` (bindable, % by panel registration order): seeds the initial
layout when provided, stays in sync while the user resizes, and an
external write re-applies the layout clamped to each panel's min/max
(collapse bookkeeping is not re-derived — parity with reference
setLayout APIs). Plumbed through the eidos wrapper (a spread can't
two-way bind, so it re-binds explicitly).
- `onResize` renamed to `onSizesChange` — with the bindable it forms the
norm pair (X + onXChange); keeping both names would alias one concept.
`onResizeEnd` stays (distinct commit moment, fires with sema
commit-set). Internal mutations funnel through emitSizesChange, which
syncs the binding and fires the callback at the same moment.
READMEs + demo updated (live sizes readout + programmatic 50/50 write,
API table). Provider test covers the pair: binding seeds over
defaultSize, drag syncs binding + callback, external write applies
clamped. Suite 4/4; browser-verified both directions (drag 50/50 -> 80/20
syncs the readout; the 50/50 chip re-applies the layout). Baseline 59.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>