diff --git a/CHANGELOG.md b/CHANGELOG.md index 542bbc9..834ceef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Choose the pause DeadAir leaves where it cuts a silence, from 0.2 to 0.5 seconds, for each show in its settings. It is 0.3 seconds unless you change it, up from 0.2, which made speech sound rushed (#182). +- Choose the pause DeadAir leaves where it cuts a silence, from 0.2 to 0.5 seconds, for each show in its settings, on a slider from brisk to relaxed shown while DeadAir is on. It is 0.3 seconds unless you change it, up from 0.2, which made speech sound rushed (#182, #183). ### Changed diff --git a/tests/ui/app.spec.js b/tests/ui/app.spec.js index d7406a6..e7f5f1c 100644 --- a/tests/ui/app.spec.js +++ b/tests/ui/app.spec.js @@ -1782,8 +1782,8 @@ test('DeadAir is off until turned on for a show, then plays it shortened, with y // A longer pause of your own (#182) saves less. const usual = (await entry()).enclosures[0].deadair; await page.locator('#content .acts [data-a="settings"]').click(); - await expect(page.locator('[name=sdpause][value="300"]')).toBeChecked(); - await page.locator('[name=sdpause][value="500"]').locator('xpath=..').click(); + await expect(page.locator('#sdpause')).toHaveValue('300'); + await page.locator('#sdpause').fill('500'); await page.locator('#ssave').click(); await expect.poll(async () => (await entry()).enclosures[0].deadair).toBeLessThan(usual); diff --git a/web/app.css b/web/app.css index 61f2805..b40e81f 100644 --- a/web/app.css +++ b/web/app.css @@ -1114,6 +1114,23 @@ input[type=range]::-moz-range-thumb{width:12px;height:12px;border:0;border-radiu .wcap{margin:0 0 16px;font-size:13px;line-height:1.55;color:var(--dim)} .wcap input[type=number]{display:inline-block;width:4.2em;margin:0 2px;padding:1px 4px;font-size:13px;text-align:center;font-variant-numeric:tabular-nums;color:var(--fg)} .ffield{display:grid;gap:6px;margin-bottom:14px} +/* DeadAir's pause: four stops on one scale, a dot at each so it reads as four places to stop. */ +.dpause{display:grid;gap:6px;margin-top:6px} +.dphead{display:flex;justify-content:space-between;align-items:baseline} +.dphead output{font-variant-numeric:tabular-nums;font-weight:600} +.dpslide{--t:22px;position:relative;height:var(--t)} +.dpslide::before,.dpslide::after{content:'';position:absolute;top:50%;pointer-events:none} +.dpslide::before{left:0;right:0;height:6px;margin-top:-3px;border-radius:3px;background:var(--raise)} +.dpslide::after{left:calc(var(--t)/2 - 3px);right:calc(var(--t)/2 - 3px);height:6px;margin-top:-3px; + background:radial-gradient(circle,var(--dim) 2px,transparent 2.5px) 0 0/6px 6px no-repeat, + radial-gradient(circle,var(--dim) 2px,transparent 2.5px) 33.333% 0/6px 6px no-repeat, + radial-gradient(circle,var(--dim) 2px,transparent 2.5px) 66.667% 0/6px 6px no-repeat, + radial-gradient(circle,var(--dim) 2px,transparent 2.5px) 100% 0/6px 6px no-repeat} +.dpslide input[type=range]{position:relative;z-index:1;width:100%;height:var(--t);margin:0;background:transparent} +.dpslide input[type=range]::-webkit-slider-thumb{width:var(--t);height:var(--t);box-shadow:0 0 0 3px var(--panel),0 1px 4px rgba(0,0,0,.3)} +.dpslide input[type=range]::-moz-range-thumb{width:var(--t);height:var(--t);box-shadow:0 0 0 3px var(--panel),0 1px 4px rgba(0,0,0,.3)} +.dpslide input[type=range]:focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:var(--t)} +.dpends{display:flex;justify-content:space-between;font-size:12.5px;color:var(--dim)} .fdl+.ffield{margin-top:18px} .flabel{font-size:14px;font-weight:500} .cap{margin:0;font-size:12.5px;line-height:1.45;color:var(--dim)} @@ -1350,6 +1367,8 @@ body:has(#player.on) #status{padding-bottom:3px} .sw input:checked+i::after{transform:translateX(20px)} .seg label{padding:7px 4px;border-radius:8px;font-size:14px} .seg label:has(input:checked){box-shadow:0 1px 3px rgba(0,0,0,.22)} + .dpslide{--t:28px} + .dpends{font-size:14px} /* 16px or more, or Safari zooms the page in on it. */ .wcap input[type=number]{font-size:16px;padding:3px 4px} .chips{background:var(--panel2);border-color:var(--edge);border-radius:10px} diff --git a/web/src/dialogs.ts b/web/src/dialogs.ts index 863e590..ea62f02 100644 --- a/web/src/dialogs.ts +++ b/web/src/dialogs.ts @@ -531,10 +531,11 @@ function settingsModal(f, newUrl?: string){
Cuts the dead air: long silences in ${isGroup?'these shows':'this show'} shrink to a beat, so episodes finish sooner and every voice keeps its own pace. Leave it off for stories whose pauses are part of the telling.
- Pause it leaves -A longer pause sounds more relaxed and saves less time.
+A longer pause sounds more relaxed and saves less time.
Whole words or phrases. Hidden from your list and not downloaded for you, along @@ -579,6 +580,10 @@ function settingsModal(f, newUrl?: string){ if(n) n.oninput=()=>{ newest=Math.max(1,Math.floor(Number(n.value))||1); draw(false); }; }; for(const r of $$('[name=swin]')) r.onchange=()=>{ mode=r.value; draw(true); }; + // The pause only matters with DeadAir on, so it is shown only then. + $('#sdead').onchange=e=>{ $('#sdpausebox').hidden=!e.target.checked; }; + const pause=$('#sdpause'), pauseText=()=>{ const s=`${pause.value/1000} s`; $('#sdpauseout').textContent=s; pause.setAttribute('aria-valuetext',s); }; + pause.oninput=pauseText; pauseText(); draw(true); // The feed's own newest items, each as tall as it is long, so the strip reads as this show. if(!isGroup) api(`/api/feeds/${encodeURIComponent(f.id)}/entries?limit=40`).then(page=>{ @@ -619,7 +624,7 @@ function settingsModal(f, newUrl?: string){ keywords:chipWords($('#skw')), max_new_per_check:mode==='default'?null:mode==='all'?0:newest, auto_download:$('#sauto').checked, allow_explicit:$('#sexp').checked, deadair:$('#sdead').checked, - deadair_pause:Number($('[name=sdpause]:checked').value)}; + deadair_pause:Number($('#sdpause').value)}; if($('#sblock')) patch.blocked=chipWords($('#sblock')); // The address is the same for everyone: an admin's to change, for a feed that moved, and the // API refuses it from anyone else. The folder and category are not this dialog's at all.