On first launch (no nodes, FTUE active), the centred “No nodes yet” / “Add a server to get started” empty-state text appeared over the top of the Terms-of-Service and PIN-entry dialogs rendered inside the avatar speech bubble.
In ClientScreen, Box children are stacked in composition order: DisplayNodes (which
contains AvatarWithSpeechBubble and its FTUE speech bubble) is the first child; the
“No nodes yet” empty-state block is the second child. Later children in a Box paint
on top of earlier ones, so the empty state physically covered the dialog.
The guard on the empty state (shouldShowNoNodesEmptyState && swarmStable) had no
awareness of the ongoing FTUE phase; it fired whenever the swarm had stabilised with no
servers or client children, which is exactly the state during initial FTUE.
Computed isFtueActive in ClientScreen by reading OnboardingStore.tosAccepted() and
ClientPinStore?.bearerToken() (the same stores AvatarWithSpeechBubble uses) and
gated the empty state on !isFtueActive. Extracted isNoNodesEmptyStateVisible() as a
testable pure helper that combines the swarm check with the stabilisation and FTUE guards.
Added four regression tests in NoNodesEmptyStateTest.
Box where an overlay (dialog, bottom sheet, bubble) is rendered first and a
full-canvas empty state is rendered second, gate the empty state on all relevant
UI-blocking conditions — including FTUE, loading, and connection phases.Box sibling order.