Adds an end-to-end auto-reconnect path so a brief network outage no
longer leaves the client wedged in a half-dead state. The flow has
three layers, each motivated by a real failure mode observed on the
Moto G live test:
* `chanora_protocol::DisconnectReason` (`UserRequested` /
`StreamEnded` / `Error(String)`) is reported on a `oneshot` when
the per-connection task exits, so the supervisor can tell user
intent apart from a real loss.
* `chanora_core` spawns a supervisor task per `ChanoraSession`. It
listens for the loss notifier AND runs a watchdog that issues
`snapshot()` probes every 5s with a 4s timeout — three consecutive
misses synthesise a `DisconnectReason::Error(...)` and trigger the
reconnect path. The watchdog catches the "ghost connected" case
where tsclientlib silently resets internal state but the event
stream never errors. Backoff schedule: 1s, 2s, 5s, 15s, 30s, 60s
(capped). On success the supervisor swaps the dead `ProtocolClient`
for the new one in place and, if audio was running, restarts the
audio engine bound to the new `voice_in`/`voice_out` channels.
* `SessionEvent` (Connected / Lost / Reconnecting / Disconnected /
AudioStarted / AudioStopped) is broadcast on a 64-slot channel.
`chanora_bridge` re-exports it as `BridgeEvent` and exposes
`events_stream(StreamSink)`; the Flutter side subscribes from
`initState` and renders a reconnect banner with attempt count and
delay. New `SnapshotProbe` exposes a clone-friendly snapshot path
so the watchdog can probe without holding `&self` across awaits.
Localization adds `statusReconnecting` and `statusConnectionLost`
keys to `app_en.arb` and `app_zh.arb`.
Verified on Moto G Stylus 5G (Android 14) against cn.teamspeak.app:
killed Wi-Fi + cellular for ~70 s; watchdog declared loss at three
misses, supervisor walked the backoff schedule, and the UI
reconnected automatically once the radios came back. Snapshot tree
re-rendered without user action.