fix(voice): amplitude meter was masked by the dross-rec keyframe animation (2.14.1)

CSS animations override normal declarations — the old box-shadow pulse painted
over the level-driven shadow. .metered now disables the fallback pulse; added
sqrt gain so speech registers visibly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-11 23:48:02 +10:00
parent 3bd8ea399c
commit 1d94dcae97
3 changed files with 15 additions and 6 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "void-server", "name": "void-server",
"version": "2.14.0", "version": "2.14.1",
"type": "module", "type": "module",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@@ -95,12 +95,18 @@ export async function renderDrossBubble() {
const analyser = actx.createAnalyser(); analyser.fftSize = 256; const analyser = actx.createAnalyser(); analyser.fftSize = 256;
src.connect(analyser); src.connect(analyser);
const buf = new Uint8Array(analyser.frequencyBinCount); const buf = new Uint8Array(analyser.frequencyBinCount);
mic.classList.add('metered'); // disables the fallback pulse; amplitude takes over
const tick = () => { const tick = () => {
if (!recording) { actx.close().catch(() => {}); mic.style.removeProperty('--voicelevel'); return; } if (!recording) {
actx.close().catch(() => {});
mic.style.removeProperty('--voicelevel'); mic.classList.remove('metered');
return;
}
analyser.getByteTimeDomainData(buf); analyser.getByteTimeDomainData(buf);
let peak = 0; let peak = 0;
for (const v of buf) peak = Math.max(peak, Math.abs(v - 128)); for (const v of buf) peak = Math.max(peak, Math.abs(v - 128));
mic.style.setProperty('--voicelevel', (peak / 128).toFixed(3)); // sqrt curve + gain: normal speech peaks ~0.10.4 raw, which read as barely-alive
mic.style.setProperty('--voicelevel', Math.min(1, Math.sqrt(peak / 48)).toFixed(3));
requestAnimationFrame(tick); requestAnimationFrame(tick);
}; };
tick(); tick();

View File

@@ -781,9 +781,12 @@ body.drawer-open #scrim { opacity: 1; pointer-events: auto; }
.dross-clip-txt{flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .dross-clip-txt{flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dross-clip audio{display:none} .dross-clip audio{display:none}
/* voice 2.14: live level ring + textarea flash (post-transcribe, no keyboard pop) */ /* voice 2.14.1: amplitude meter. .metered kills the keyframe pulse — CSS animations
.dross-mic.rec{position:relative;box-shadow:0 0 0 calc(2px + 14px * var(--voicelevel, 0)) rgba(255,79,46,calc(0.12 + 0.45 * var(--voicelevel, 0)));transition:box-shadow 90ms linear} override normal declarations, so the old dross-rec box-shadow was masking the meter. */
.dross-mic.rec svg{transform:scale(calc(1 + 0.35 * var(--voicelevel, 0)));transition:transform 90ms linear} .dross-mic.rec.metered{animation:none;position:relative;
box-shadow:0 0 0 calc(2px + 22px * var(--voicelevel, 0)) rgba(255,79,46,calc(0.15 + 0.5 * var(--voicelevel, 0)));
transition:box-shadow 70ms linear}
.dross-mic.rec.metered svg{transform:scale(calc(1 + 0.5 * var(--voicelevel, 0)));transition:transform 70ms linear}
.dross-inwrap textarea{overflow-y:auto;max-height:120px;transition:height 120ms ease} .dross-inwrap textarea{overflow-y:auto;max-height:120px;transition:height 120ms ease}
.dross-inwrap textarea.flash{border-color:var(--dross-glow);box-shadow:0 0 0 2px var(--dross-soft)} .dross-inwrap textarea.flash{border-color:var(--dross-glow);box-shadow:0 0 0 2px var(--dross-soft)}