The root component had absorbed every page's UI state because of re-render
folklore; the characterization suite (tests/reactor_semantics.rs) replaced the
folklore with measured rules at the current windows-reactor pin: a child's sync
use_state re-renders its owner reliably, including from backend-wired event
handlers (MenuFlyout clicks, pointer enter/exit, ComboBox selection) and under
element-equal wrappers. Only a background thread's AsyncSetState write is still
dropped. So user-event-driven page state moves into the pages that own it:
* hosts_page: forget, rename and hover become local sync use_state; the
HostsProps fields, their PartialEq arms and the root plumbing are gone.
* settings_page: converted from a hook-free 14-parameter fn into a real
component(...) with a SettingsProps (data-only PartialEq, the HostsProps
pattern). Scope, the delete confirmation, the edit-modal flag and the repaint
revision are its own sync use_state; commit() and the row helpers take the
sync setter now.
What deliberately stays rooted, each with its reason in place: show_add and the
settings section drive root tween workers (a thread writing root async state
can only be started by the owner of the trigger), the tween progress values are
those workers' output, and everything thread-fed (discovery, HUD, probes,
pads, deep links, speed, library) keeps the async-drop rule. window size stays
a root read so resize re-renders the tree.
One deliberate behaviour shift: page-local state now resets when its page
unmounts, so re-entering Settings lands on the Default settings scope (the
section still persists, being root state).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>