mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-12 05:30:12 +02:00
add setting for lyrics container padding
This commit is contained in:
@@ -552,6 +552,8 @@
|
|||||||
"lyricOffset": "Lyrics offset (ms)",
|
"lyricOffset": "Lyrics offset (ms)",
|
||||||
"lyricGap": "Lyric gap",
|
"lyricGap": "Lyric gap",
|
||||||
"lyricLineLeadTime": "Line lead time (ms)",
|
"lyricLineLeadTime": "Line lead time (ms)",
|
||||||
|
"lyricPaddingLeft": "Lyrics left padding (%)",
|
||||||
|
"lyricPaddingRight": "Lyrics right padding (%)",
|
||||||
"lyricSize": "Lyric size",
|
"lyricSize": "Lyric size",
|
||||||
"lyricOpacityNonActive": "Non-active lyric opacity",
|
"lyricOpacityNonActive": "Non-active lyric opacity",
|
||||||
"lyricScaleNonActive": "Non-active lyric scale",
|
"lyricScaleNonActive": "Non-active lyric scale",
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
.content {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
@media screen and (orientation: portrait) {
|
||||||
|
padding-top: 5vh !important;
|
||||||
|
padding-right: var(--lyric-padding-right, 0%) !important;
|
||||||
|
padding-bottom: 5vh !important;
|
||||||
|
padding-left: var(--lyric-padding-left, 0%) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import clsx from 'clsx';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import styles from './lyrics-scroll-content.module.css';
|
||||||
|
|
||||||
|
export interface LyricsScrollContentProps {
|
||||||
|
bottomScrollPadding?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
gap?: number;
|
||||||
|
paddingLeft?: number;
|
||||||
|
paddingRight?: number;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LyricsScrollContent = ({
|
||||||
|
bottomScrollPadding = '50vh',
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
gap,
|
||||||
|
paddingLeft = 0,
|
||||||
|
paddingRight = 0,
|
||||||
|
style,
|
||||||
|
}: LyricsScrollContentProps) => {
|
||||||
|
const contentStyle = useMemo(
|
||||||
|
() =>
|
||||||
|
({
|
||||||
|
'--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, style],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx(styles.content, className)} style={contentStyle}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -147,6 +147,40 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
|
|||||||
`${t('page.fullscreenPlayer.config.lyricGap')} (${t('page.fullscreenPlayer.config.unsynchronized')})`,
|
`${t('page.fullscreenPlayer.config.lyricGap')} (${t('page.fullscreenPlayer.config.unsynchronized')})`,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
control: (
|
||||||
|
<Slider
|
||||||
|
defaultValue={displaySettings.paddingLeft ?? 0}
|
||||||
|
label={(value) => `${value}%`}
|
||||||
|
max={50}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={(value) => {
|
||||||
|
updateDisplaySetting({ paddingLeft: value });
|
||||||
|
}}
|
||||||
|
step={1}
|
||||||
|
w={100}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
description: '',
|
||||||
|
title: t('page.fullscreenPlayer.config.lyricPaddingLeft'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
control: (
|
||||||
|
<Slider
|
||||||
|
defaultValue={displaySettings.paddingRight ?? 0}
|
||||||
|
label={(value) => `${value}%`}
|
||||||
|
max={50}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={(value) => {
|
||||||
|
updateDisplaySetting({ paddingRight: value });
|
||||||
|
}}
|
||||||
|
step={1}
|
||||||
|
w={100}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
description: '',
|
||||||
|
title: t('page.fullscreenPlayer.config.lyricPaddingRight'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
control: (
|
control: (
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ export const useSynchronizedLyricsBase = (settingsKey = 'default', offsetMs?: nu
|
|||||||
: 24,
|
: 24,
|
||||||
gap: displaySettings.gap && displaySettings.gap !== 0 ? displaySettings.gap : 24,
|
gap: displaySettings.gap && displaySettings.gap !== 0 ? displaySettings.gap : 24,
|
||||||
opacityNonActive: displaySettings.opacityNonActive,
|
opacityNonActive: displaySettings.opacityNonActive,
|
||||||
|
paddingLeft: displaySettings.paddingLeft ?? 0,
|
||||||
|
paddingRight: displaySettings.paddingRight ?? 0,
|
||||||
scaleNonActive:
|
scaleNonActive:
|
||||||
displaySettings.scaleNonActive && displaySettings.scaleNonActive !== 0
|
displaySettings.scaleNonActive && displaySettings.scaleNonActive !== 0
|
||||||
? displaySettings.scaleNonActive
|
? displaySettings.scaleNonActive
|
||||||
@@ -135,9 +137,8 @@ export const useSynchronizedLyricsBase = (settingsKey = 'default', offsetMs?: nu
|
|||||||
'--lyric-opacity': settings.opacityNonActive,
|
'--lyric-opacity': settings.opacityNonActive,
|
||||||
'--lyric-scale': settings.scaleNonActive,
|
'--lyric-scale': settings.scaleNonActive,
|
||||||
'--lyric-scale-origin': settings.alignment,
|
'--lyric-scale-origin': settings.alignment,
|
||||||
gap: `${settings.gap}px`,
|
|
||||||
}) as React.CSSProperties,
|
}) as React.CSSProperties,
|
||||||
[settings.alignment, settings.gap, settings.opacityNonActive, settings.scaleNonActive],
|
[settings.alignment, settings.opacityNonActive, settings.scaleNonActive],
|
||||||
);
|
);
|
||||||
|
|
||||||
const hideScrollbar = useCallback(() => {
|
const hideScrollbar = useCallback(() => {
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 10vh 0 50vh;
|
|
||||||
overflow: hidden auto;
|
overflow: hidden auto;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
@media screen and (orientation: portrait) {
|
|
||||||
padding: 5vh 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
getLyricLineText,
|
getLyricLineText,
|
||||||
normalizeLyrics,
|
normalizeLyrics,
|
||||||
} from '/@/renderer/features/lyrics/api/lyrics-utils';
|
} from '/@/renderer/features/lyrics/api/lyrics-utils';
|
||||||
|
import { LyricsScrollContent } from '/@/renderer/features/lyrics/components/lyrics-scroll-content';
|
||||||
import { SyncedRomajiLyrics } from '/@/renderer/features/lyrics/hooks/use-furigana-lyrics';
|
import { SyncedRomajiLyrics } from '/@/renderer/features/lyrics/hooks/use-furigana-lyrics';
|
||||||
import { useLyricsAnimationEngine } from '/@/renderer/features/lyrics/hooks/use-lyrics-animation-engine';
|
import { useLyricsAnimationEngine } from '/@/renderer/features/lyrics/hooks/use-lyrics-animation-engine';
|
||||||
import {
|
import {
|
||||||
@@ -290,68 +291,74 @@ export const SynchronizedKaraokeLyrics = ({
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{ ...containerStyle, ...style }}
|
style={{ ...containerStyle, ...style }}
|
||||||
>
|
>
|
||||||
{settings.showProvider && source && (
|
<LyricsScrollContent
|
||||||
<LyricLine
|
gap={settings.gap}
|
||||||
alignment={settings.alignment}
|
paddingLeft={settings.paddingLeft}
|
||||||
className="lyric-credit"
|
paddingRight={settings.paddingRight}
|
||||||
fontSize={settings.fontSize}
|
>
|
||||||
text={`Provided by ${source}`}
|
{settings.showProvider && source && (
|
||||||
/>
|
<LyricLine
|
||||||
)}
|
alignment={settings.alignment}
|
||||||
{settings.showMatch && remote && (
|
className="lyric-credit"
|
||||||
<LyricLine
|
fontSize={settings.fontSize}
|
||||||
alignment={settings.alignment}
|
text={`Provided by ${source}`}
|
||||||
className="lyric-credit"
|
/>
|
||||||
fontSize={settings.fontSize}
|
)}
|
||||||
text={`"${name} by ${artist}"`}
|
{settings.showMatch && remote && (
|
||||||
/>
|
<LyricLine
|
||||||
)}
|
alignment={settings.alignment}
|
||||||
{normalizedLyrics.map((rawLine, idx) => {
|
className="lyric-credit"
|
||||||
const lineStartMs = getLyricLineStartMs(rawLine);
|
fontSize={settings.fontSize}
|
||||||
const pronunciationText = getOverlayText(
|
text={`"${name} by ${artist}"`}
|
||||||
pronunciationLyrics,
|
/>
|
||||||
lineStartMs,
|
)}
|
||||||
romajiLyrics?.[idx] ? getLyricLineText(romajiLyrics[idx]) : undefined,
|
{normalizedLyrics.map((rawLine, idx) => {
|
||||||
);
|
const lineStartMs = getLyricLineStartMs(rawLine);
|
||||||
const translationText = getOverlayText(
|
const pronunciationText = getOverlayText(
|
||||||
translationLyrics,
|
pronunciationLyrics,
|
||||||
lineStartMs,
|
lineStartMs,
|
||||||
translatedLyrics?.split('\n')[idx],
|
romajiLyrics?.[idx] ? getLyricLineText(romajiLyrics[idx]) : undefined,
|
||||||
);
|
);
|
||||||
|
const translationText = getOverlayText(
|
||||||
|
translationLyrics,
|
||||||
|
lineStartMs,
|
||||||
|
translatedLyrics?.split('\n')[idx],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!rawLine.cueLines?.length) {
|
||||||
|
return (
|
||||||
|
<LyricLine
|
||||||
|
alignment={settings.alignment}
|
||||||
|
className="lyric-line synchronized"
|
||||||
|
data-lyric-time={lineStartMs}
|
||||||
|
fontSize={settings.fontSize}
|
||||||
|
id={`karaoke-line-${idx}`}
|
||||||
|
key={idx}
|
||||||
|
romajiText={pronunciationText}
|
||||||
|
text={getLyricLineText(rawLine)}
|
||||||
|
translatedText={translationText}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!rawLine.cueLines?.length) {
|
|
||||||
return (
|
return (
|
||||||
<LyricLine
|
<KaraokeLyricLine
|
||||||
|
agents={agents}
|
||||||
alignment={settings.alignment}
|
alignment={settings.alignment}
|
||||||
className="lyric-line synchronized"
|
className="synchronized"
|
||||||
|
cueLines={rawLine.cueLines}
|
||||||
data-lyric-time={lineStartMs}
|
data-lyric-time={lineStartMs}
|
||||||
fontSize={settings.fontSize}
|
fontSize={settings.fontSize}
|
||||||
id={`karaoke-line-${idx}`}
|
id={`karaoke-line-${idx}`}
|
||||||
key={idx}
|
key={idx}
|
||||||
|
lineIndex={idx}
|
||||||
|
romajiCueLines={syncedRomajiLyrics?.[idx] ?? null}
|
||||||
romajiText={pronunciationText}
|
romajiText={pronunciationText}
|
||||||
text={getLyricLineText(rawLine)}
|
|
||||||
translatedText={translationText}
|
translatedText={translationText}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
})}
|
||||||
|
</LyricsScrollContent>
|
||||||
return (
|
|
||||||
<KaraokeLyricLine
|
|
||||||
agents={agents}
|
|
||||||
alignment={settings.alignment}
|
|
||||||
className="synchronized"
|
|
||||||
cueLines={rawLine.cueLines}
|
|
||||||
data-lyric-time={lineStartMs}
|
|
||||||
fontSize={settings.fontSize}
|
|
||||||
id={`karaoke-line-${idx}`}
|
|
||||||
key={idx}
|
|
||||||
lineIndex={idx}
|
|
||||||
romajiCueLines={syncedRomajiLyrics?.[idx] ?? null}
|
|
||||||
romajiText={pronunciationText}
|
|
||||||
translatedText={translationText}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,12 +4,7 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 10vh 0 50vh;
|
|
||||||
overflow: hidden auto;
|
overflow: hidden auto;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
transform: translateY(-2rem);
|
transform: translateY(-2rem);
|
||||||
|
|
||||||
@media screen and (orientation: portrait) {
|
|
||||||
padding: 5vh 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
getLyricLineText,
|
getLyricLineText,
|
||||||
normalizeLyrics,
|
normalizeLyrics,
|
||||||
} from '/@/renderer/features/lyrics/api/lyrics-utils';
|
} from '/@/renderer/features/lyrics/api/lyrics-utils';
|
||||||
|
import { LyricsScrollContent } from '/@/renderer/features/lyrics/components/lyrics-scroll-content';
|
||||||
import { useLyricsAnimationEngine } from '/@/renderer/features/lyrics/hooks/use-lyrics-animation-engine';
|
import { useLyricsAnimationEngine } from '/@/renderer/features/lyrics/hooks/use-lyrics-animation-engine';
|
||||||
import {
|
import {
|
||||||
LYRICS_SCROLL_CONTAINER_ID,
|
LYRICS_SCROLL_CONTAINER_ID,
|
||||||
@@ -235,50 +236,56 @@ export const SynchronizedLyrics = ({
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
style={{ ...containerStyle, ...style }}
|
style={{ ...containerStyle, ...style }}
|
||||||
>
|
>
|
||||||
{settings.showProvider && source && (
|
<LyricsScrollContent
|
||||||
<LyricLine
|
gap={settings.gap}
|
||||||
alignment={settings.alignment}
|
paddingLeft={settings.paddingLeft}
|
||||||
className="lyric-credit"
|
paddingRight={settings.paddingRight}
|
||||||
fontSize={settings.fontSize}
|
>
|
||||||
text={`Provided by ${source}`}
|
{settings.showProvider && source && (
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{settings.showMatch && remote && (
|
|
||||||
<LyricLine
|
|
||||||
alignment={settings.alignment}
|
|
||||||
className="lyric-credit"
|
|
||||||
fontSize={settings.fontSize}
|
|
||||||
text={`"${name} by ${artist}"`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{normalizedLyrics.map((rawLine, idx) => {
|
|
||||||
const lineStartMs = getLyricLineStartMs(rawLine);
|
|
||||||
const lineText = getLyricLineText(rawLine);
|
|
||||||
const pronunciationText = getOverlayText(
|
|
||||||
pronunciationLyrics,
|
|
||||||
lineStartMs,
|
|
||||||
romajiLyrics?.[idx] ? getLyricLineText(romajiLyrics[idx]) : undefined,
|
|
||||||
);
|
|
||||||
const translationText = getOverlayText(
|
|
||||||
translationLyrics,
|
|
||||||
lineStartMs,
|
|
||||||
translatedLyrics?.split('\n')[idx],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<LyricLine
|
<LyricLine
|
||||||
alignment={settings.alignment}
|
alignment={settings.alignment}
|
||||||
className="lyric-line synchronized"
|
className="lyric-credit"
|
||||||
data-lyric-time={lineStartMs}
|
|
||||||
fontSize={settings.fontSize}
|
fontSize={settings.fontSize}
|
||||||
id={`lyric-${idx}`}
|
text={`Provided by ${source}`}
|
||||||
key={idx}
|
|
||||||
romajiText={pronunciationText}
|
|
||||||
text={lineText}
|
|
||||||
translatedText={translationText}
|
|
||||||
/>
|
/>
|
||||||
);
|
)}
|
||||||
})}
|
{settings.showMatch && remote && (
|
||||||
|
<LyricLine
|
||||||
|
alignment={settings.alignment}
|
||||||
|
className="lyric-credit"
|
||||||
|
fontSize={settings.fontSize}
|
||||||
|
text={`"${name} by ${artist}"`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{normalizedLyrics.map((rawLine, idx) => {
|
||||||
|
const lineStartMs = getLyricLineStartMs(rawLine);
|
||||||
|
const lineText = getLyricLineText(rawLine);
|
||||||
|
const pronunciationText = getOverlayText(
|
||||||
|
pronunciationLyrics,
|
||||||
|
lineStartMs,
|
||||||
|
romajiLyrics?.[idx] ? getLyricLineText(romajiLyrics[idx]) : undefined,
|
||||||
|
);
|
||||||
|
const translationText = getOverlayText(
|
||||||
|
translationLyrics,
|
||||||
|
lineStartMs,
|
||||||
|
translatedLyrics?.split('\n')[idx],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LyricLine
|
||||||
|
alignment={settings.alignment}
|
||||||
|
className="lyric-line synchronized"
|
||||||
|
data-lyric-time={lineStartMs}
|
||||||
|
fontSize={settings.fontSize}
|
||||||
|
id={`lyric-${idx}`}
|
||||||
|
key={idx}
|
||||||
|
romajiText={pronunciationText}
|
||||||
|
text={lineText}
|
||||||
|
translatedText={translationText}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</LyricsScrollContent>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,11 +4,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 10vh 0 6vh;
|
|
||||||
overflow: hidden auto;
|
overflow: hidden auto;
|
||||||
transform: translateY(-2rem);
|
transform: translateY(-2rem);
|
||||||
|
|
||||||
@media screen and (orientation: portrait) {
|
|
||||||
padding: 5vh 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import styles from './unsynchronized-lyrics.module.css';
|
import styles from './unsynchronized-lyrics.module.css';
|
||||||
|
|
||||||
|
import { LyricsScrollContent } from '/@/renderer/features/lyrics/components/lyrics-scroll-content';
|
||||||
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
|
import { LyricLine } from '/@/renderer/features/lyrics/lyric-line';
|
||||||
import { useLyricsDisplaySettings, useLyricsSettings } from '/@/renderer/store';
|
import { useLyricsDisplaySettings, useLyricsSettings } from '/@/renderer/store';
|
||||||
import { FullLyricsMetadata } from '/@/shared/types/domain-types';
|
import { FullLyricsMetadata } from '/@/shared/types/domain-types';
|
||||||
@@ -49,35 +50,42 @@ export const UnsynchronizedLyrics = ({
|
|||||||
}, [romajiLyrics]);
|
}, [romajiLyrics]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} style={{ gap: `${settings.gapUnsync}px` }}>
|
<div className={styles.container}>
|
||||||
{settings.showProvider && source && (
|
<LyricsScrollContent
|
||||||
<LyricLine
|
bottomScrollPadding="6vh"
|
||||||
alignment={settings.alignment}
|
gap={settings.gapUnsync}
|
||||||
className="lyric-credit"
|
paddingLeft={displaySettings.paddingLeft ?? 0}
|
||||||
fontSize={settings.fontSizeUnsync}
|
paddingRight={displaySettings.paddingRight ?? 0}
|
||||||
text={`Provided by ${source}`}
|
>
|
||||||
/>
|
{settings.showProvider && source && (
|
||||||
)}
|
<LyricLine
|
||||||
{settings.showMatch && remote && (
|
alignment={settings.alignment}
|
||||||
<LyricLine
|
className="lyric-credit"
|
||||||
alignment={settings.alignment}
|
fontSize={settings.fontSizeUnsync}
|
||||||
className="lyric-credit"
|
text={`Provided by ${source}`}
|
||||||
fontSize={settings.fontSizeUnsync}
|
/>
|
||||||
text={`"${name} by ${artist}"`}
|
)}
|
||||||
/>
|
{settings.showMatch && remote && (
|
||||||
)}
|
<LyricLine
|
||||||
{lines.map((text, idx) => (
|
alignment={settings.alignment}
|
||||||
<LyricLine
|
className="lyric-credit"
|
||||||
alignment={settings.alignment}
|
fontSize={settings.fontSizeUnsync}
|
||||||
className="lyric-line unsynchronized"
|
text={`"${name} by ${artist}"`}
|
||||||
fontSize={settings.fontSizeUnsync}
|
/>
|
||||||
id={`lyric-${idx}`}
|
)}
|
||||||
key={idx}
|
{lines.map((text, idx) => (
|
||||||
romajiText={romajiLines[idx]}
|
<LyricLine
|
||||||
text={text}
|
alignment={settings.alignment}
|
||||||
translatedText={translatedLines[idx]}
|
className="lyric-line unsynchronized"
|
||||||
/>
|
fontSize={settings.fontSizeUnsync}
|
||||||
))}
|
id={`lyric-${idx}`}
|
||||||
|
key={idx}
|
||||||
|
romajiText={romajiLines[idx]}
|
||||||
|
text={text}
|
||||||
|
translatedText={translatedLines[idx]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</LyricsScrollContent>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -248,7 +248,14 @@ const Controls = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleLyricsSettings = (property: string, value: any) => {
|
const handleLyricsSettings = (property: string, value: any) => {
|
||||||
const displayProperties = ['fontSize', 'fontSizeUnsync', 'gap', 'gapUnsync'];
|
const displayProperties = [
|
||||||
|
'fontSize',
|
||||||
|
'fontSizeUnsync',
|
||||||
|
'gap',
|
||||||
|
'gapUnsync',
|
||||||
|
'paddingLeft',
|
||||||
|
'paddingRight',
|
||||||
|
];
|
||||||
if (displayProperties.includes(property)) {
|
if (displayProperties.includes(property)) {
|
||||||
const currentDisplay = useSettingsStore.getState().lyricsDisplay;
|
const currentDisplay = useSettingsStore.getState().lyricsDisplay;
|
||||||
setSettings({
|
setSettings({
|
||||||
@@ -475,6 +482,38 @@ const Controls = () => {
|
|||||||
</Group>
|
</Group>
|
||||||
</Option.Control>
|
</Option.Control>
|
||||||
</Option>
|
</Option>
|
||||||
|
<Option>
|
||||||
|
<Option.Label>
|
||||||
|
{t('page.fullscreenPlayer.config.lyricPaddingLeft')}
|
||||||
|
</Option.Label>
|
||||||
|
<Option.Control>
|
||||||
|
<Slider
|
||||||
|
defaultValue={lyricConfig.paddingLeft ?? 0}
|
||||||
|
label={(value) => `${value}%`}
|
||||||
|
max={20}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={(value) => handleLyricsSettings('paddingLeft', value)}
|
||||||
|
step={1}
|
||||||
|
w="100%"
|
||||||
|
/>
|
||||||
|
</Option.Control>
|
||||||
|
</Option>
|
||||||
|
<Option>
|
||||||
|
<Option.Label>
|
||||||
|
{t('page.fullscreenPlayer.config.lyricPaddingRight')}
|
||||||
|
</Option.Label>
|
||||||
|
<Option.Control>
|
||||||
|
<Slider
|
||||||
|
defaultValue={lyricConfig.paddingRight ?? 0}
|
||||||
|
label={(value) => `${value}%`}
|
||||||
|
max={20}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={(value) => handleLyricsSettings('paddingRight', value)}
|
||||||
|
step={1}
|
||||||
|
w="100%"
|
||||||
|
/>
|
||||||
|
</Option.Control>
|
||||||
|
</Option>
|
||||||
<Option>
|
<Option>
|
||||||
<Option.Label>
|
<Option.Label>
|
||||||
{t('page.fullscreenPlayer.config.lyricAlignment')}
|
{t('page.fullscreenPlayer.config.lyricAlignment')}
|
||||||
|
|||||||
@@ -51,7 +51,14 @@ export const MobileFullscreenPlayerHeader = memo(
|
|||||||
const lyricConfig = { ...lyricsSettings, ...displaySettings };
|
const lyricConfig = { ...lyricsSettings, ...displaySettings };
|
||||||
|
|
||||||
const handleLyricsSettings = (property: string, value: any) => {
|
const handleLyricsSettings = (property: string, value: any) => {
|
||||||
const displayProperties = ['fontSize', 'fontSizeUnsync', 'gap', 'gapUnsync'];
|
const displayProperties = [
|
||||||
|
'fontSize',
|
||||||
|
'fontSizeUnsync',
|
||||||
|
'gap',
|
||||||
|
'gapUnsync',
|
||||||
|
'paddingLeft',
|
||||||
|
'paddingRight',
|
||||||
|
];
|
||||||
if (displayProperties.includes(property)) {
|
if (displayProperties.includes(property)) {
|
||||||
const currentDisplay = useSettingsStore.getState().lyricsDisplay;
|
const currentDisplay = useSettingsStore.getState().lyricsDisplay;
|
||||||
setSettings({
|
setSettings({
|
||||||
@@ -284,6 +291,42 @@ export const MobileFullscreenPlayerHeader = memo(
|
|||||||
</Group>
|
</Group>
|
||||||
</Option.Control>
|
</Option.Control>
|
||||||
</Option>
|
</Option>
|
||||||
|
<Option>
|
||||||
|
<Option.Label>
|
||||||
|
{t('page.fullscreenPlayer.config.lyricPaddingLeft')}
|
||||||
|
</Option.Label>
|
||||||
|
<Option.Control>
|
||||||
|
<Slider
|
||||||
|
defaultValue={lyricConfig.paddingLeft ?? 0}
|
||||||
|
label={(value) => `${value}%`}
|
||||||
|
max={20}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={(value) =>
|
||||||
|
handleLyricsSettings('paddingLeft', value)
|
||||||
|
}
|
||||||
|
step={1}
|
||||||
|
w="100%"
|
||||||
|
/>
|
||||||
|
</Option.Control>
|
||||||
|
</Option>
|
||||||
|
<Option>
|
||||||
|
<Option.Label>
|
||||||
|
{t('page.fullscreenPlayer.config.lyricPaddingRight')}
|
||||||
|
</Option.Label>
|
||||||
|
<Option.Control>
|
||||||
|
<Slider
|
||||||
|
defaultValue={lyricConfig.paddingRight ?? 0}
|
||||||
|
label={(value) => `${value}%`}
|
||||||
|
max={20}
|
||||||
|
min={0}
|
||||||
|
onChangeEnd={(value) =>
|
||||||
|
handleLyricsSettings('paddingRight', value)
|
||||||
|
}
|
||||||
|
step={1}
|
||||||
|
w="100%"
|
||||||
|
/>
|
||||||
|
</Option.Control>
|
||||||
|
</Option>
|
||||||
<Option>
|
<Option>
|
||||||
<Option.Label>
|
<Option.Label>
|
||||||
{t('page.fullscreenPlayer.config.lyricAlignment')}
|
{t('page.fullscreenPlayer.config.lyricAlignment')}
|
||||||
|
|||||||
@@ -570,6 +570,8 @@ const LyricsDisplaySettingsSchema = z.object({
|
|||||||
gap: z.number(),
|
gap: z.number(),
|
||||||
gapUnsync: z.number(),
|
gapUnsync: z.number(),
|
||||||
opacityNonActive: z.number(),
|
opacityNonActive: z.number(),
|
||||||
|
paddingLeft: z.number(),
|
||||||
|
paddingRight: z.number(),
|
||||||
scaleNonActive: z.number(),
|
scaleNonActive: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1872,6 +1874,8 @@ const initialState: SettingsState = {
|
|||||||
gap: 24,
|
gap: 24,
|
||||||
gapUnsync: 24,
|
gapUnsync: 24,
|
||||||
opacityNonActive: 0.2,
|
opacityNonActive: 0.2,
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
scaleNonActive: 0.95,
|
scaleNonActive: 0.95,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -2539,10 +2543,25 @@ export const useSettingsStore = createWithEqualityFn<SettingsSlice>()(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 30) {
|
||||||
|
for (const [key, displaySettings] of Object.entries(state.lyricsDisplay)) {
|
||||||
|
const legacySettings = displaySettings as typeof displaySettings & {
|
||||||
|
paddingX?: number;
|
||||||
|
};
|
||||||
|
const legacyPaddingX = legacySettings.paddingX ?? 0;
|
||||||
|
|
||||||
|
state.lyricsDisplay[key] = {
|
||||||
|
...displaySettings,
|
||||||
|
paddingLeft: displaySettings.paddingLeft ?? legacyPaddingX,
|
||||||
|
paddingRight: displaySettings.paddingRight ?? legacyPaddingX,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return persistedState;
|
return persistedState;
|
||||||
},
|
},
|
||||||
name: 'store_settings',
|
name: 'store_settings',
|
||||||
version: 28,
|
version: 30,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user