feat(call): mic level meter on the mute button (#146)

While unmuted, the microphone button (call view and the bottom call bar)
shows three small green bars in the icon's corner driven by the fork's
io.lotus.mic_level (element-call v0.25.0-lotus.16), so you can see the mic is
actually picking you up. Nothing is drawn while muted or silent; the button's
label is unchanged (the bars are decorative).

Your own name no longer appears in the "… is speaking" line: that is now for
everyone else, and your voice is on the meter. Avatar speaking rings still
include you, and the "You're muted" talking-while-muted notice is unchanged.

Verified in a local call with a fake-tone mic: data-mic-level cycles 3/2/1
with the tone on both buttons, disappears on mute and returns on unmute; the
status line reads "bob is speaking..." only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-25 12:09:34 -04:00
co-authored by Claude Opus 5.5
parent 75d55e861b
commit 3449338b3e
7 changed files with 123 additions and 11 deletions
+8 -2
View File
@@ -1,6 +1,7 @@
import React from 'react';
import { Icon, IconButton, Icons, Line, Text, Tooltip, TooltipProvider } from 'folds';
import { useAtom } from 'jotai';
import { MicLevelBars } from './MicLevelBars';
import * as css from './styles.css';
import { MobileTouchTarget } from '../../styles/mobile.css';
import { callChatAtom } from '../../state/callEmbed';
@@ -15,8 +16,10 @@ export function ControlDivider() {
type MicrophoneButtonProps = {
enabled: boolean;
onToggle: () => Promise<unknown>;
/** [Gitea #146] Your input level, 0–3 bars; shown only while unmuted. */
level?: number;
};
export function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
export function MicrophoneButton({ enabled, onToggle, level = 0 }: MicrophoneButtonProps) {
const [micState, toggleMic] = useAsyncCallback(onToggle);
const loading = micState.status === AsyncStatus.Loading;
@@ -43,7 +46,10 @@ export function MicrophoneButton({ enabled, onToggle }: MicrophoneButtonProps) {
outlined
disabled={loading}
>
<Icon size="400" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
<span style={{ position: 'relative', display: 'inline-flex' }}>
<Icon size="400" src={enabled ? Icons.Mic : Icons.MicMute} filled={!enabled} />
{enabled && <MicLevelBars level={level} />}
</span>
</IconButton>
)}
</TooltipProvider>