Files
jellybloom/src/pages/settings/sections/Audio.tsx
T
2026-03-31 09:53:25 +03:00

134 lines
5.8 KiB
TypeScript

import { Section, Row, SubHeading, Toggle, Input, Segmented, type PrefsLike } from '../_ui'
import { subtitleClasses } from '../../../lib/subtitle-style'
export function AudioSection({ prefs }: { prefs: PrefsLike }) {
const preview = subtitleClasses({
subtitleFontSize: prefs.subtitleFontSize,
subtitleFontFamily: prefs.subtitleFontFamily,
subtitleBackground: prefs.subtitleBackground,
subtitleEdge: prefs.subtitleEdge,
subtitlePosition: prefs.subtitlePosition,
subtitleColor: prefs.subtitleColor,
})
const previewClass = preview.className
.replace(/\babsolute\b|\binset-x-0\b|\bbottom-32\b|\btop-24\b/g, '')
return (
<Section id="audio" title="Audio & Subtitles" description="Default tracks for new playback sessions">
<SubHeading label="Languages & loudness" />
<Row label="Subtitle mode" hint="When subtitles should appear by default">
<Segmented
value={prefs.subtitleMode}
onChange={v => prefs.setPreference('subtitleMode', v)}
options={[
{ value: 'none', label: 'Off' },
{ value: 'default', label: 'Default' },
{ value: 'always', label: 'Always' },
]}
/>
</Row>
<Row label="Subtitle language" hint="ISO 639 code, e.g. eng, fra, spa">
<Input value={prefs.subtitleLanguage} onChange={v => prefs.setPreference('subtitleLanguage', v)} placeholder="eng" width="w-24" />
</Row>
<Row label="Audio language" hint="ISO 639 code, e.g. eng, jpn, fra">
<Input value={prefs.audioLanguage} onChange={v => prefs.setPreference('audioLanguage', v)} placeholder="eng" width="w-24" />
</Row>
<Row label="Volume boost" hint="Allow volume past 100% via Web Audio gain (1.0 - 3.0)">
<Segmented
value={String(prefs.volumeBoost)}
onChange={v => prefs.setPreference('volumeBoost', Number(v))}
options={[
{ value: '1', label: '100%' },
{ value: '1.5', label: '150%' },
{ value: '2', label: '200%' },
{ value: '3', label: '300%' },
]}
/>
</Row>
<Row label="Night mode" hint="Compress dynamic range - lifts dialogue, tames action">
<Toggle value={prefs.nightMode} onChange={v => prefs.setPreference('nightMode', v)} />
</Row>
<Row label="Audio passthrough" hint="Request bitstream passthrough for Dolby TrueHD / DTS-HD MA. Requires receiver support and may not work in all browsers.">
<Toggle value={prefs.audioPassthrough} onChange={v => prefs.setPreference('audioPassthrough', v)} />
</Row>
<SubHeading label="Subtitle styling" />
<Row label={`Subtitle size (${prefs.subtitleFontSize}px)`} hint="Caption text size">
<input
type="range"
min={12}
max={72}
step={1}
value={prefs.subtitleFontSize}
onChange={e => prefs.setPreference('subtitleFontSize', Number(e.target.value))}
className="subtitle-size-slider w-full max-w-[200px] h-7 appearance-none bg-transparent cursor-pointer"
/>
</Row>
<Row label="Background" hint="Box behind the caption text">
<Segmented
value={prefs.subtitleBackground}
onChange={v => prefs.setPreference('subtitleBackground', v)}
options={[
{ value: 'none', label: 'None' },
{ value: 'subtle', label: 'Subtle' },
{ value: 'solid', label: 'Solid' },
]}
/>
</Row>
<Row label="Text edge" hint="Shadow or outline to separate text from the picture">
<Segmented
value={prefs.subtitleEdge}
onChange={v => prefs.setPreference('subtitleEdge', v)}
options={[
{ value: 'none', label: 'None' },
{ value: 'shadow', label: 'Shadow' },
{ value: 'outline', label: 'Outline' },
]}
/>
</Row>
<Row label="Position" hint="Where captions sit on screen">
<Segmented
value={prefs.subtitlePosition}
onChange={v => prefs.setPreference('subtitlePosition', v)}
options={[
{ value: 'bottom', label: 'Bottom' },
{ value: 'top', label: 'Top' },
]}
/>
</Row>
<Row label="Text colour" hint="Caption colour - pick what stays readable on your content">
<Segmented
value={prefs.subtitleColor}
onChange={v => prefs.setPreference('subtitleColor', v)}
options={[
{ value: 'white', label: 'White' },
{ value: 'yellow', label: 'Yellow' },
{ value: 'cyan', label: 'Cyan' },
]}
/>
</Row>
<Row label="Font family" hint="Typeface used for caption text">
<Segmented
value={prefs.subtitleFontFamily}
onChange={v => prefs.setPreference('subtitleFontFamily', v)}
options={[
{ value: 'sans', label: 'Sans' },
{ value: 'serif', label: 'Display' },
{ value: 'mono', label: 'Mono' },
]}
/>
</Row>
<div className="pt-2 pb-1">
<p className="text-[11.5px] uppercase tracking-[0.14em] text-text-3 mb-2 font-medium">Preview</p>
<div className="relative h-44 rounded-xl overflow-hidden border border-border bg-[radial-gradient(ellipse_at_30%_20%,rgba(245,182,66,0.12),transparent_55%),radial-gradient(ellipse_at_75%_80%,rgba(64,80,120,0.35),transparent_60%),linear-gradient(180deg,#1a1610_0%,#0c0a08_100%)]">
<div className={`flex ${prefs.subtitlePosition === 'top' ? 'items-start pt-5' : 'items-end pb-5'} justify-center h-full px-6`}>
<div className={previewClass} style={preview.style}>
<span data-cue>The signal cuts through the static, just for a moment.</span>
</div>
</div>
</div>
</div>
</Section>
)
}