use custom lyrics styling on search form instead of setting

This commit is contained in:
jeffvli
2026-07-19 16:27:49 -07:00
parent e4378d1bc9
commit dbd82d46cf
10 changed files with 152 additions and 100 deletions
@@ -11,3 +11,7 @@
padding-left: var(--lyric-padding-left, 0%) !important; padding-left: var(--lyric-padding-left, 0%) !important;
} }
} }
.preview {
padding: 0 !important;
}
@@ -10,6 +10,7 @@ export interface LyricsScrollContentProps {
gap?: number; gap?: number;
paddingLeft?: number; paddingLeft?: number;
paddingRight?: number; paddingRight?: number;
preview?: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;
} }
@@ -20,25 +21,35 @@ export const LyricsScrollContent = ({
gap, gap,
paddingLeft = 0, paddingLeft = 0,
paddingRight = 0, paddingRight = 0,
preview = false,
style, style,
}: LyricsScrollContentProps) => { }: LyricsScrollContentProps) => {
const contentStyle = useMemo( const contentStyle = useMemo(() => {
() => if (preview) {
({ return {
'--lyric-padding-left': `${paddingLeft}%`,
'--lyric-padding-right': `${paddingRight}%`,
gap: gap !== undefined ? `${gap}px` : undefined, gap: gap !== undefined ? `${gap}px` : undefined,
paddingBottom: bottomScrollPadding, padding: 0,
paddingLeft: `${paddingLeft}%`,
paddingRight: `${paddingRight}%`,
paddingTop: '10vh',
...style, ...style,
}) as React.CSSProperties, } as React.CSSProperties;
[bottomScrollPadding, gap, paddingLeft, paddingRight, style], }
);
return {
'--lyric-padding-left': `${paddingLeft}%`,
'--lyric-padding-right': `${paddingRight}%`,
gap: gap !== undefined ? `${gap}px` : undefined,
paddingBottom: bottomScrollPadding,
paddingLeft: `${paddingLeft}%`,
paddingRight: `${paddingRight}%`,
paddingTop: '10vh',
...style,
} as React.CSSProperties;
}, [bottomScrollPadding, gap, paddingLeft, paddingRight, preview, style]);
return ( return (
<div className={clsx(styles.content, className)} style={contentStyle}> <div
className={clsx(styles.content, preview && styles.preview, className)}
style={contentStyle}
>
{children} {children}
</div> </div>
); );
@@ -33,22 +33,3 @@
} }
} }
} }
.lyrics-preview {
:global(.synchronized-lyrics) {
height: auto !important;
padding: 1rem 0 !important;
overflow: visible !important;
transform: none !important;
}
}
.lyrics-content-wrapper {
:global(> div) {
height: auto !important;
max-height: none !important;
padding: 1rem 0 !important;
overflow: visible !important;
transform: none !important;
}
}
@@ -246,7 +246,6 @@ export const LyricsSearchForm = ({ artist, name, onSearchOverride }: LyricSearch
{selectedResult && ( {selectedResult && (
<Stack style={{ flex: 1, height: '100%', minHeight: 0, overflow: 'hidden' }}> <Stack style={{ flex: 1, height: '100%', minHeight: 0, overflow: 'hidden' }}>
<ScrollArea <ScrollArea
className={styles['lyrics-preview']}
style={{ style={{
height: '100%', height: '100%',
paddingRight: '1rem', paddingRight: '1rem',
@@ -255,46 +254,42 @@ export const LyricsSearchForm = ({ artist, name, onSearchOverride }: LyricSearch
{isPreviewLoading ? ( {isPreviewLoading ? (
<Spinner container /> <Spinner container />
) : previewData ? ( ) : previewData ? (
<div Array.isArray(previewData) ? (
className={styles['lyrics-content-wrapper']} lyricsHasWordCues(previewData) ? (
style={{ width: '100%' }} <SynchronizedKaraokeLyrics
> preview
{Array.isArray(previewData) ? (
lyricsHasWordCues(previewData) ? (
<SynchronizedKaraokeLyrics
style={{ padding: 0 }}
{...({
artist: selectedResult.artist,
lyrics: previewData,
name: selectedResult.name,
remote: true,
source: selectedResult.source,
} as SynchronizedLyricsProps)}
/>
) : (
<SynchronizedLyrics
style={{ padding: 0 }}
{...({
artist: selectedResult.artist,
lyrics: previewData,
name: selectedResult.name,
remote: true,
source: selectedResult.source,
} as SynchronizedLyricsProps)}
/>
)
) : (
<UnsynchronizedLyrics
{...({ {...({
artist: selectedResult.artist, artist: selectedResult.artist,
lyrics: previewData, lyrics: previewData,
name: selectedResult.name, name: selectedResult.name,
remote: true, remote: true,
source: selectedResult.source, source: selectedResult.source,
} as UnsynchronizedLyricsProps)} } as SynchronizedLyricsProps)}
/> />
)} ) : (
</div> <SynchronizedLyrics
preview
{...({
artist: selectedResult.artist,
lyrics: previewData,
name: selectedResult.name,
remote: true,
source: selectedResult.source,
} as SynchronizedLyricsProps)}
/>
)
) : (
<UnsynchronizedLyrics
preview
{...({
artist: selectedResult.artist,
lyrics: previewData,
name: selectedResult.name,
remote: true,
source: selectedResult.source,
} as UnsynchronizedLyricsProps)}
/>
)
) : ( ) : (
<Center> <Center>
<Text isMuted>{t('page.fullscreenPlayer.noLyrics')}</Text> <Text isMuted>{t('page.fullscreenPlayer.noLyrics')}</Text>
@@ -7,3 +7,11 @@
overflow: hidden auto; overflow: hidden auto;
word-break: break-all; word-break: break-all;
} }
.preview {
height: auto;
max-height: none;
padding: 1rem 0;
overflow: visible;
transform: none;
}
@@ -34,6 +34,7 @@ export interface SynchronizedKaraokeLyricsProps extends Omit<FullLyricsMetadata,
extraOverlayLyrics?: SynchronizedLyricsData[]; extraOverlayLyrics?: SynchronizedLyricsData[];
lyrics: SynchronizedLyricsData; lyrics: SynchronizedLyricsData;
offsetMs?: number; offsetMs?: number;
preview?: boolean;
pronunciationLyrics?: null | SynchronizedLyricsData; pronunciationLyrics?: null | SynchronizedLyricsData;
romajiLyrics?: null | SynchronizedLyricsData; romajiLyrics?: null | SynchronizedLyricsData;
settingsKey?: string; settingsKey?: string;
@@ -44,6 +45,8 @@ export interface SynchronizedKaraokeLyricsProps extends Omit<FullLyricsMetadata,
} }
const SEEK_DETECT_THRESHOLD_MS = 500; const SEEK_DETECT_THRESHOLD_MS = 500;
const PREVIEW_FONT_SIZE = 20;
const PREVIEW_GAP = 20;
export const SynchronizedKaraokeLyrics = ({ export const SynchronizedKaraokeLyrics = ({
agents, agents,
@@ -52,6 +55,7 @@ export const SynchronizedKaraokeLyrics = ({
lyrics, lyrics,
name, name,
offsetMs, offsetMs,
preview = false,
pronunciationLyrics, pronunciationLyrics,
romajiLyrics, romajiLyrics,
settingsKey = 'default', settingsKey = 'default',
@@ -77,6 +81,11 @@ export const SynchronizedKaraokeLyrics = ({
showScrollbar, showScrollbar,
} = useSynchronizedLyricsBase(settingsKey, offsetMs); } = useSynchronizedLyricsBase(settingsKey, offsetMs);
const effectiveFontSize = preview ? PREVIEW_FONT_SIZE : settings.fontSize;
const effectiveGap = preview ? PREVIEW_GAP : settings.gap;
const effectivePaddingLeft = preview ? 0 : settings.paddingLeft;
const effectivePaddingRight = preview ? 0 : settings.paddingRight;
const normalizedLyrics = useMemo(() => normalizeLyrics(lyrics), [lyrics]); const normalizedLyrics = useMemo(() => normalizeLyrics(lyrics), [lyrics]);
const rafRef = useRef<null | number>(null); const rafRef = useRef<null | number>(null);
const statusRef = useRef(usePlayerStoreBase.getState().player.status); const statusRef = useRef(usePlayerStoreBase.getState().player.status);
@@ -97,13 +106,13 @@ export const SynchronizedKaraokeLyrics = ({
containerRef, containerRef,
followRef, followRef,
followScrollAlignmentRef, followScrollAlignmentRef,
fontSize: settings.fontSize, fontSize: effectiveFontSize,
gap: settings.gap, gap: effectiveGap,
lineIdPrefix: 'karaoke-line', lineIdPrefix: 'karaoke-line',
lineLeadTimeMsRef, lineLeadTimeMsRef,
lyrics: normalizedLyrics, lyrics: normalizedLyrics,
paddingLeft: settings.paddingLeft, paddingLeft: effectivePaddingLeft,
paddingRight: settings.paddingRight, paddingRight: effectivePaddingRight,
scrollContainerId: LYRICS_SCROLL_CONTAINER_ID, scrollContainerId: LYRICS_SCROLL_CONTAINER_ID,
}); });
@@ -328,7 +337,11 @@ export const SynchronizedKaraokeLyrics = ({
return ( return (
<div <div
className={clsx(styles.container, 'synchronized-karaoke-lyrics overlay-scrollbar')} className={clsx(
styles.container,
preview && styles.preview,
'synchronized-karaoke-lyrics overlay-scrollbar',
)}
id={LYRICS_SCROLL_CONTAINER_ID} id={LYRICS_SCROLL_CONTAINER_ID}
onClick={handleContainerClick} onClick={handleContainerClick}
onMouseEnter={showScrollbar} onMouseEnter={showScrollbar}
@@ -337,15 +350,16 @@ export const SynchronizedKaraokeLyrics = ({
style={{ ...containerStyle, ...style }} style={{ ...containerStyle, ...style }}
> >
<LyricsScrollContent <LyricsScrollContent
gap={settings.gap} gap={effectiveGap}
paddingLeft={settings.paddingLeft} paddingLeft={effectivePaddingLeft}
paddingRight={settings.paddingRight} paddingRight={effectivePaddingRight}
preview={preview}
> >
{settings.showProvider && source && ( {settings.showProvider && source && (
<LyricLine <LyricLine
alignment={settings.alignment} alignment={settings.alignment}
className="lyric-credit" className="lyric-credit"
fontSize={settings.fontSize} fontSize={effectiveFontSize}
text={`${source}`} text={`${source}`}
/> />
)} )}
@@ -353,7 +367,7 @@ export const SynchronizedKaraokeLyrics = ({
<LyricLine <LyricLine
alignment={settings.alignment} alignment={settings.alignment}
className="lyric-credit" className="lyric-credit"
fontSize={settings.fontSize} fontSize={effectiveFontSize}
text={`${name}${artist}`} text={`${name}${artist}`}
/> />
)} )}
@@ -396,7 +410,7 @@ export const SynchronizedKaraokeLyrics = ({
alignment={settings.alignment} alignment={settings.alignment}
className="lyric-line synchronized" className="lyric-line synchronized"
data-lyric-time={lineStartMs} data-lyric-time={lineStartMs}
fontSize={settings.fontSize} fontSize={effectiveFontSize}
id={`karaoke-line-${idx}`} id={`karaoke-line-${idx}`}
key={idx} key={idx}
romajiText={pronunciationText} romajiText={pronunciationText}
@@ -414,7 +428,7 @@ export const SynchronizedKaraokeLyrics = ({
cueLines={rawLine.cueLines} cueLines={rawLine.cueLines}
data-lyric-time={lineStartMs} data-lyric-time={lineStartMs}
extraOverlays={extraOverlays} extraOverlays={extraOverlays}
fontSize={settings.fontSize} fontSize={effectiveFontSize}
id={`karaoke-line-${idx}`} id={`karaoke-line-${idx}`}
key={idx} key={idx}
lineIndex={idx} lineIndex={idx}
@@ -8,3 +8,11 @@
word-break: break-all; word-break: break-all;
transform: translateY(-2rem); transform: translateY(-2rem);
} }
.preview {
height: auto;
max-height: none;
padding: 1rem 0;
overflow: visible;
transform: none;
}
@@ -29,6 +29,7 @@ export interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyric
extraOverlayLyrics?: SynchronizedLyricsData[]; extraOverlayLyrics?: SynchronizedLyricsData[];
lyrics: SynchronizedLyricsData; lyrics: SynchronizedLyricsData;
offsetMs?: number; offsetMs?: number;
preview?: boolean;
pronunciationLyrics?: null | SynchronizedLyricsData; pronunciationLyrics?: null | SynchronizedLyricsData;
romajiLyrics?: null | SynchronizedLyricsData; romajiLyrics?: null | SynchronizedLyricsData;
settingsKey?: string; settingsKey?: string;
@@ -38,12 +39,15 @@ export interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyric
} }
const SEEK_DETECT_THRESHOLD_MS = 500; const SEEK_DETECT_THRESHOLD_MS = 500;
const PREVIEW_FONT_SIZE = 20;
const PREVIEW_GAP = 20;
export const SynchronizedLyrics = ({ export const SynchronizedLyrics = ({
artist, artist,
lyrics, lyrics,
name, name,
offsetMs, offsetMs,
preview = false,
pronunciationLyrics, pronunciationLyrics,
romajiLyrics, romajiLyrics,
settingsKey = 'default', settingsKey = 'default',
@@ -68,6 +72,11 @@ export const SynchronizedLyrics = ({
showScrollbar, showScrollbar,
} = useSynchronizedLyricsBase(settingsKey, offsetMs); } = useSynchronizedLyricsBase(settingsKey, offsetMs);
const effectiveFontSize = preview ? PREVIEW_FONT_SIZE : settings.fontSize;
const effectiveGap = preview ? PREVIEW_GAP : settings.gap;
const effectivePaddingLeft = preview ? 0 : settings.paddingLeft;
const effectivePaddingRight = preview ? 0 : settings.paddingRight;
const normalizedLyrics = useMemo(() => normalizeLyrics(lyrics), [lyrics]); const normalizedLyrics = useMemo(() => normalizeLyrics(lyrics), [lyrics]);
const rafRef = useRef<null | number>(null); const rafRef = useRef<null | number>(null);
const statusRef = useRef(usePlayerStoreBase.getState().player.status); const statusRef = useRef(usePlayerStoreBase.getState().player.status);
@@ -83,13 +92,13 @@ export const SynchronizedLyrics = ({
containerRef, containerRef,
followRef, followRef,
followScrollAlignmentRef, followScrollAlignmentRef,
fontSize: settings.fontSize, fontSize: effectiveFontSize,
gap: settings.gap, gap: effectiveGap,
lineIdPrefix: 'lyric', lineIdPrefix: 'lyric',
lineLeadTimeMsRef, lineLeadTimeMsRef,
lyrics: normalizedLyrics, lyrics: normalizedLyrics,
paddingLeft: settings.paddingLeft, paddingLeft: effectivePaddingLeft,
paddingRight: settings.paddingRight, paddingRight: effectivePaddingRight,
scrollContainerId: LYRICS_SCROLL_CONTAINER_ID, scrollContainerId: LYRICS_SCROLL_CONTAINER_ID,
}); });
@@ -240,7 +249,11 @@ export const SynchronizedLyrics = ({
return ( return (
<div <div
className={clsx(styles.container, 'synchronized-lyrics overlay-scrollbar')} className={clsx(
styles.container,
preview && styles.preview,
'synchronized-lyrics overlay-scrollbar',
)}
id={LYRICS_SCROLL_CONTAINER_ID} id={LYRICS_SCROLL_CONTAINER_ID}
onClick={handleContainerClick} onClick={handleContainerClick}
onMouseEnter={showScrollbar} onMouseEnter={showScrollbar}
@@ -249,15 +262,16 @@ export const SynchronizedLyrics = ({
style={{ ...containerStyle, ...style }} style={{ ...containerStyle, ...style }}
> >
<LyricsScrollContent <LyricsScrollContent
gap={settings.gap} gap={effectiveGap}
paddingLeft={settings.paddingLeft} paddingLeft={effectivePaddingLeft}
paddingRight={settings.paddingRight} paddingRight={effectivePaddingRight}
preview={preview}
> >
{settings.showProvider && source && ( {settings.showProvider && source && (
<LyricLine <LyricLine
alignment={settings.alignment} alignment={settings.alignment}
className="lyric-credit" className="lyric-credit"
fontSize={settings.fontSize} fontSize={effectiveFontSize}
text={`${source}`} text={`${source}`}
/> />
)} )}
@@ -265,7 +279,7 @@ export const SynchronizedLyrics = ({
<LyricLine <LyricLine
alignment={settings.alignment} alignment={settings.alignment}
className="lyric-credit" className="lyric-credit"
fontSize={settings.fontSize} fontSize={effectiveFontSize}
text={`${name}${artist}`} text={`${name}${artist}`}
/> />
)} )}
@@ -290,7 +304,7 @@ export const SynchronizedLyrics = ({
alignment={settings.alignment} alignment={settings.alignment}
className="lyric-line synchronized" className="lyric-line synchronized"
data-lyric-time={lineStartMs} data-lyric-time={lineStartMs}
fontSize={settings.fontSize} fontSize={effectiveFontSize}
id={`lyric-${idx}`} id={`lyric-${idx}`}
key={idx} key={idx}
romajiText={pronunciationText} romajiText={pronunciationText}
@@ -7,3 +7,11 @@
overflow: hidden auto; overflow: hidden auto;
transform: translateY(-2rem); transform: translateY(-2rem);
} }
.preview {
height: auto;
max-height: none;
padding: 1rem 0;
overflow: visible;
transform: none;
}
@@ -1,3 +1,4 @@
import clsx from 'clsx';
import { useMemo } from 'react'; import { useMemo } from 'react';
import styles from './unsynchronized-lyrics.module.css'; import styles from './unsynchronized-lyrics.module.css';
@@ -9,15 +10,20 @@ import { FullLyricsMetadata } from '/@/shared/types/domain-types';
export interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> { export interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
lyrics: string; lyrics: string;
preview?: boolean;
romajiLyrics?: null | string; romajiLyrics?: null | string;
settingsKey?: string; settingsKey?: string;
translatedLyrics?: null | string; translatedLyrics?: null | string;
} }
const PREVIEW_FONT_SIZE = 20;
const PREVIEW_GAP = 20;
export const UnsynchronizedLyrics = ({ export const UnsynchronizedLyrics = ({
artist, artist,
lyrics, lyrics,
name, name,
preview = false,
romajiLyrics, romajiLyrics,
settingsKey = 'default', settingsKey = 'default',
source, source,
@@ -27,14 +33,16 @@ export const UnsynchronizedLyrics = ({
const displaySettings = useLyricsDisplaySettings(settingsKey); const displaySettings = useLyricsDisplaySettings(settingsKey);
const settings = { const settings = {
...lyricsSettings, ...lyricsSettings,
fontSizeUnsync: fontSizeUnsync: preview
displaySettings.fontSizeUnsync && displaySettings.fontSizeUnsync !== 0 ? PREVIEW_FONT_SIZE
? displaySettings.fontSizeUnsync : displaySettings.fontSizeUnsync && displaySettings.fontSizeUnsync !== 0
: 24, ? displaySettings.fontSizeUnsync
gapUnsync: : 24,
displaySettings.gapUnsync && displaySettings.gapUnsync !== 0 gapUnsync: preview
? displaySettings.gapUnsync ? PREVIEW_GAP
: 24, : displaySettings.gapUnsync && displaySettings.gapUnsync !== 0
? displaySettings.gapUnsync
: 24,
}; };
const lines = useMemo(() => { const lines = useMemo(() => {
return lyrics.split('\n'); return lyrics.split('\n');
@@ -49,12 +57,13 @@ export const UnsynchronizedLyrics = ({
}, [romajiLyrics]); }, [romajiLyrics]);
return ( return (
<div className={styles.container}> <div className={clsx(styles.container, preview && styles.preview)}>
<LyricsScrollContent <LyricsScrollContent
bottomScrollPadding="6vh" bottomScrollPadding="6vh"
gap={settings.gapUnsync} gap={settings.gapUnsync}
paddingLeft={displaySettings.paddingLeft ?? 0} paddingLeft={preview ? 0 : (displaySettings.paddingLeft ?? 0)}
paddingRight={displaySettings.paddingRight ?? 0} paddingRight={preview ? 0 : (displaySettings.paddingRight ?? 0)}
preview={preview}
> >
{settings.showProvider && source && ( {settings.showProvider && source && (
<LyricLine <LyricLine