-
+
Radio & Settings
{SETTINGS_SECTION_LABELS[settingsSection]}
-
+
{
+ // Focus the panel when it becomes visible
+ if (showCracker && el) {
+ const focusable = el.querySelector('input, button:not([disabled])');
+ if (focusable) setTimeout(() => focusable.focus(), 210);
+ }
+ }}
className={cn(
'border-t border-border bg-background transition-all duration-200 overflow-hidden',
showCracker ? 'h-[275px]' : 'h-0'
diff --git a/frontend/src/components/BotCodeEditor.tsx b/frontend/src/components/BotCodeEditor.tsx
index 33e716b..486f603 100644
--- a/frontend/src/components/BotCodeEditor.tsx
+++ b/frontend/src/components/BotCodeEditor.tsx
@@ -25,6 +25,7 @@ export function BotCodeEditor({ value, onChange, id, height = '256px' }: BotCode
}}
className="text-sm"
id={id}
+ aria-label="Bot code editor"
/>
);
diff --git a/frontend/src/components/ChannelInfoPane.tsx b/frontend/src/components/ChannelInfoPane.tsx
index 2614de1..26eeb6d 100644
--- a/frontend/src/components/ChannelInfoPane.tsx
+++ b/frontend/src/components/ChannelInfoPane.tsx
@@ -3,7 +3,7 @@ import { api } from '../api';
import { formatTime } from '../utils/messageParser';
import { isFavorite } from '../utils/favorites';
import { handleKeyboardActivate } from '../utils/a11y';
-import { Sheet, SheetContent, SheetHeader, SheetTitle } from './ui/sheet';
+import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from './ui/sheet';
import { toast } from './ui/sonner';
import type { Channel, ChannelDetail, Favorite } from '../types';
@@ -65,6 +65,7 @@ export function ChannelInfoPane({
Channel Info
+ Channel details and statistics
{loading && !detail ? (
diff --git a/frontend/src/components/ChatHeader.tsx b/frontend/src/components/ChatHeader.tsx
index fbfebd4..414264b 100644
--- a/frontend/src/components/ChatHeader.tsx
+++ b/frontend/src/components/ChatHeader.tsx
@@ -117,6 +117,7 @@ export function ChatHeader({
);
}}
title="Click to copy"
+ aria-label={conversation.type === 'channel' ? 'Copy channel key' : 'Copy contact key'}
>
{conversation.type === 'channel' ? conversation.id.toLowerCase() : conversation.id}
diff --git a/frontend/src/components/ContactInfoPane.tsx b/frontend/src/components/ContactInfoPane.tsx
index c071b78..d89e078 100644
--- a/frontend/src/components/ContactInfoPane.tsx
+++ b/frontend/src/components/ContactInfoPane.tsx
@@ -6,7 +6,7 @@ import { getMapFocusHash } from '../utils/urlHash';
import { isFavorite } from '../utils/favorites';
import { handleKeyboardActivate } from '../utils/a11y';
import { ContactAvatar } from './ContactAvatar';
-import { Sheet, SheetContent, SheetHeader, SheetTitle } from './ui/sheet';
+import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from './ui/sheet';
import { toast } from './ui/sonner';
import type { Contact, ContactDetail, Favorite, RadioConfig } from '../types';
@@ -98,6 +98,7 @@ export function ContactInfoPane({
Contact Info
+ Contact details and actions
{isNameOnly && nameOnlyValue ? (
diff --git a/frontend/src/components/CrackerPanel.tsx b/frontend/src/components/CrackerPanel.tsx
index 4342f5e..73e0110 100644
--- a/frontend/src/components/CrackerPanel.tsx
+++ b/frontend/src/components/CrackerPanel.tsx
@@ -586,7 +586,14 @@ export function CrackerPanel({
: `${Math.round(progress.etaSeconds / 60)}m`}
-