Extends the existing ScheduledBroadcasts page with an Announcements
section (list + create/edit form) over the announcement_routes.py API
from c0572b04. No new page/route/nav entry.
- AnnouncementsPanel: list (plain-English schedule summary, channel
summary, enabled toggle, last sent, delete-with-confirm) + a
create/edit form (message textarea with a live char count against
the 140-char budget, schedule-kind-specific controls, Preview button
wired to POST /announcements/{id}/preview -- never sends).
- AnnouncementChannelPicker: polls GET /api/channels (Meshtastic) and
GET /api/meshcore/channels/detail (MeshCore) and renders both as
checkbox groups, mixing transports freely. Degrades per radio: shows
a plain "unreachable" note and still renders any already-selected
channel by its stored name so an existing announcement stays
editable even when a radio is down. Blocks save on zero channels.
- New announcements always save disabled (enabled is never sent from
the create/edit form); the list-row toggle is the only place this
form family arms one, matching the backend contract where PUT is the
sole place `enabled` can change.
- lib/api.ts: fetchMeshtasticChannels + the Announcement CRUD/preview
client functions, following the existing fetchJson/detail-surfacing
error conventions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>