diff --git a/src/frontend/src/features/rooms/livekit/components/ParticipantTile.tsx b/src/frontend/src/features/rooms/livekit/components/ParticipantTile.tsx index b19c6f33..b02d954a 100644 --- a/src/frontend/src/features/rooms/livekit/components/ParticipantTile.tsx +++ b/src/frontend/src/features/rooms/livekit/components/ParticipantTile.tsx @@ -31,6 +31,7 @@ import { FullScreenShareWarning } from './FullScreenShareWarning' import { ParticipantName } from './ParticipantName' import { getParticipantName } from '@/features/rooms/utils/getParticipantName' import { useTranslation } from 'react-i18next' +import { css } from '@/styled-system/css' export function TrackRefContextIfNeeded( props: React.PropsWithChildren<{ @@ -109,14 +110,8 @@ export const ParticipantTile: ( const participantName = getParticipantName(trackReference.participant) const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' }) - // Avoid double announcements: LiveKit's useParticipantTile may set its own - // aria-label / aria-labelledby / aria-describedby. We strip them here - // and provide a single, explicit label for the focusable tile container. - const { 'aria-label': _ignoredAriaLabel, ...safeElementProps } = elementProps - void _ignoredAriaLabel - const interactiveProps = { - ...safeElementProps, + ...elementProps, // Ensure the tile is focusable to expose contextual controls to keyboard users. tabIndex: 0, 'aria-label': t('containerLabel', { name: participantName }), @@ -232,6 +227,23 @@ export const ParticipantTile: ( )} + {hasKeyboardFocus && ( +
+ {t('toolbarHint')} +
+ )} ) }) diff --git a/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/DesktopControlBar.tsx b/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/DesktopControlBar.tsx index c7b050e2..dce26463 100644 --- a/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/DesktopControlBar.tsx +++ b/src/frontend/src/features/rooms/livekit/prefabs/ControlBar/DesktopControlBar.tsx @@ -11,6 +11,7 @@ import { OptionsButton } from '../../components/controls/Options/OptionsButton' import { StartMediaButton } from '../../components/controls/StartMediaButton' import { MoreOptions } from './MoreOptions' import { useRef } from 'react' +import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut' import { VideoDeviceControl } from '../../components/controls/Device/VideoDeviceControl' import { AudioDevicesControl } from '../../components/controls/Device/AudioDevicesControl' @@ -19,6 +20,18 @@ export function DesktopControlBar({ }: Readonly) { const browserSupportsScreenSharing = supportsScreenSharing() const desktopControlBarEl = useRef(null) + + useRegisterKeyboardShortcut({ + shortcut: { key: 'F2' }, + handler: () => { + const root = desktopControlBarEl.current + if (!root) return + const firstButton = root.querySelector( + 'button, [role="button"], [tabindex="0"]' + ) + firstButton?.focus() + }, + }) return (