diff --git a/package.json b/package.json index b915b26..aaadf58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "void-server", - "version": "2.14.0", + "version": "2.14.1", "type": "module", "private": true, "scripts": { diff --git a/public/components/dross_bubble.js b/public/components/dross_bubble.js index dd3933c..e6d5a62 100644 --- a/public/components/dross_bubble.js +++ b/public/components/dross_bubble.js @@ -95,12 +95,18 @@ export async function renderDrossBubble() { const analyser = actx.createAnalyser(); analyser.fftSize = 256; src.connect(analyser); const buf = new Uint8Array(analyser.frequencyBinCount); + mic.classList.add('metered'); // disables the fallback pulse; amplitude takes over 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); let peak = 0; 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.1–0.4 raw, which read as barely-alive + mic.style.setProperty('--voicelevel', Math.min(1, Math.sqrt(peak / 48)).toFixed(3)); requestAnimationFrame(tick); }; tick(); diff --git a/public/style.css b/public/style.css index dfa2483..9114c3c 100644 --- a/public/style.css +++ b/public/style.css @@ -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 audio{display:none} -/* voice 2.14: live level ring + textarea flash (post-transcribe, no keyboard pop) */ -.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} -.dross-mic.rec svg{transform:scale(calc(1 + 0.35 * var(--voicelevel, 0)));transition:transform 90ms linear} +/* voice 2.14.1: amplitude meter. .metered kills the keyframe pulse — CSS animations + override normal declarations, so the old dross-rec box-shadow was masking the meter. */ +.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.flash{border-color:var(--dross-glow);box-shadow:0 0 0 2px var(--dross-soft)}