From bdcc88cbfe19516003875f3bb974c60bd5a8a98f Mon Sep 17 00:00:00 2001 From: jeffvli Date: Thu, 9 Jul 2026 01:20:11 -0700 Subject: [PATCH] add setting for lyrics container padding --- src/i18n/locales/en.json | 2 + .../lyrics-scroll-content.module.css | 13 +++ .../components/lyrics-scroll-content.tsx | 45 ++++++++ .../components/lyrics-settings-form.tsx | 34 ++++++ .../hooks/use-synchronized-lyrics-base.ts | 5 +- .../synchronized-karaoke-lyrics.module.css | 5 - .../lyrics/synchronized-karaoke-lyrics.tsx | 109 ++++++++++-------- .../lyrics/synchronized-lyrics.module.css | 5 - .../features/lyrics/synchronized-lyrics.tsx | 87 +++++++------- .../lyrics/unsynchronized-lyrics.module.css | 5 - .../features/lyrics/unsynchronized-lyrics.tsx | 66 ++++++----- .../player/components/full-screen-player.tsx | 41 ++++++- .../mobile-fullscreen-player-header.tsx | 45 +++++++- src/renderer/store/settings.store.ts | 21 +++- 14 files changed, 343 insertions(+), 140 deletions(-) create mode 100644 src/renderer/features/lyrics/components/lyrics-scroll-content.module.css create mode 100644 src/renderer/features/lyrics/components/lyrics-scroll-content.tsx diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b5a25fd0c..58cb4359e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -552,6 +552,8 @@ "lyricOffset": "Lyrics offset (ms)", "lyricGap": "Lyric gap", "lyricLineLeadTime": "Line lead time (ms)", + "lyricPaddingLeft": "Lyrics left padding (%)", + "lyricPaddingRight": "Lyrics right padding (%)", "lyricSize": "Lyric size", "lyricOpacityNonActive": "Non-active lyric opacity", "lyricScaleNonActive": "Non-active lyric scale", diff --git a/src/renderer/features/lyrics/components/lyrics-scroll-content.module.css b/src/renderer/features/lyrics/components/lyrics-scroll-content.module.css new file mode 100644 index 000000000..18fc85fdc --- /dev/null +++ b/src/renderer/features/lyrics/components/lyrics-scroll-content.module.css @@ -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; + } +} diff --git a/src/renderer/features/lyrics/components/lyrics-scroll-content.tsx b/src/renderer/features/lyrics/components/lyrics-scroll-content.tsx new file mode 100644 index 000000000..e6a014536 --- /dev/null +++ b/src/renderer/features/lyrics/components/lyrics-scroll-content.tsx @@ -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 ( +
+ {children} +
+ ); +}; diff --git a/src/renderer/features/lyrics/components/lyrics-settings-form.tsx b/src/renderer/features/lyrics/components/lyrics-settings-form.tsx index bce14c847..741d920e2 100644 --- a/src/renderer/features/lyrics/components/lyrics-settings-form.tsx +++ b/src/renderer/features/lyrics/components/lyrics-settings-form.tsx @@ -147,6 +147,40 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) => `${t('page.fullscreenPlayer.config.lyricGap')} (${t('page.fullscreenPlayer.config.unsynchronized')})`, ), }, + { + control: ( + `${value}%`} + max={50} + min={0} + onChangeEnd={(value) => { + updateDisplaySetting({ paddingLeft: value }); + }} + step={1} + w={100} + /> + ), + description: '', + title: t('page.fullscreenPlayer.config.lyricPaddingLeft'), + }, + { + control: ( + `${value}%`} + max={50} + min={0} + onChangeEnd={(value) => { + updateDisplaySetting({ paddingRight: value }); + }} + step={1} + w={100} + /> + ), + description: '', + title: t('page.fullscreenPlayer.config.lyricPaddingRight'), + }, { control: ( { diff --git a/src/renderer/features/lyrics/synchronized-karaoke-lyrics.module.css b/src/renderer/features/lyrics/synchronized-karaoke-lyrics.module.css index 98b03e0cd..c6bc21817 100644 --- a/src/renderer/features/lyrics/synchronized-karaoke-lyrics.module.css +++ b/src/renderer/features/lyrics/synchronized-karaoke-lyrics.module.css @@ -4,11 +4,6 @@ flex-direction: column; width: 100%; min-height: 0; - padding: 10vh 0 50vh; overflow: hidden auto; word-break: break-all; - - @media screen and (orientation: portrait) { - padding: 5vh 0; - } } diff --git a/src/renderer/features/lyrics/synchronized-karaoke-lyrics.tsx b/src/renderer/features/lyrics/synchronized-karaoke-lyrics.tsx index c17483f32..76e9fa47a 100644 --- a/src/renderer/features/lyrics/synchronized-karaoke-lyrics.tsx +++ b/src/renderer/features/lyrics/synchronized-karaoke-lyrics.tsx @@ -9,6 +9,7 @@ import { getLyricLineText, normalizeLyrics, } 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 { useLyricsAnimationEngine } from '/@/renderer/features/lyrics/hooks/use-lyrics-animation-engine'; import { @@ -290,68 +291,74 @@ export const SynchronizedKaraokeLyrics = ({ ref={containerRef} style={{ ...containerStyle, ...style }} > - {settings.showProvider && source && ( - - )} - {settings.showMatch && remote && ( - - )} - {normalizedLyrics.map((rawLine, idx) => { - const lineStartMs = getLyricLineStartMs(rawLine); - const pronunciationText = getOverlayText( - pronunciationLyrics, - lineStartMs, - romajiLyrics?.[idx] ? getLyricLineText(romajiLyrics[idx]) : undefined, - ); - const translationText = getOverlayText( - translationLyrics, - lineStartMs, - translatedLyrics?.split('\n')[idx], - ); + + {settings.showProvider && source && ( + + )} + {settings.showMatch && remote && ( + + )} + {normalizedLyrics.map((rawLine, idx) => { + const lineStartMs = getLyricLineStartMs(rawLine); + const pronunciationText = getOverlayText( + pronunciationLyrics, + lineStartMs, + romajiLyrics?.[idx] ? getLyricLineText(romajiLyrics[idx]) : undefined, + ); + const translationText = getOverlayText( + translationLyrics, + lineStartMs, + translatedLyrics?.split('\n')[idx], + ); + + if (!rawLine.cueLines?.length) { + return ( + + ); + } - if (!rawLine.cueLines?.length) { return ( - ); - } - - return ( - - ); - })} + })} + ); }; diff --git a/src/renderer/features/lyrics/synchronized-lyrics.module.css b/src/renderer/features/lyrics/synchronized-lyrics.module.css index f7b5a8e37..7f722d293 100644 --- a/src/renderer/features/lyrics/synchronized-lyrics.module.css +++ b/src/renderer/features/lyrics/synchronized-lyrics.module.css @@ -4,12 +4,7 @@ flex-direction: column; width: 100%; min-height: 0; - padding: 10vh 0 50vh; overflow: hidden auto; word-break: break-all; transform: translateY(-2rem); - - @media screen and (orientation: portrait) { - padding: 5vh 0; - } } diff --git a/src/renderer/features/lyrics/synchronized-lyrics.tsx b/src/renderer/features/lyrics/synchronized-lyrics.tsx index 24165a2d6..28e24cdde 100644 --- a/src/renderer/features/lyrics/synchronized-lyrics.tsx +++ b/src/renderer/features/lyrics/synchronized-lyrics.tsx @@ -9,6 +9,7 @@ import { getLyricLineText, normalizeLyrics, } 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 { LYRICS_SCROLL_CONTAINER_ID, @@ -235,50 +236,56 @@ export const SynchronizedLyrics = ({ ref={containerRef} style={{ ...containerStyle, ...style }} > - {settings.showProvider && source && ( - - )} - {settings.showMatch && remote && ( - - )} - {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 ( + + {settings.showProvider && source && ( - ); - })} + )} + {settings.showMatch && remote && ( + + )} + {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 ( + + ); + })} + ); }; diff --git a/src/renderer/features/lyrics/unsynchronized-lyrics.module.css b/src/renderer/features/lyrics/unsynchronized-lyrics.module.css index f207f1adf..4f2133c7a 100644 --- a/src/renderer/features/lyrics/unsynchronized-lyrics.module.css +++ b/src/renderer/features/lyrics/unsynchronized-lyrics.module.css @@ -4,11 +4,6 @@ flex-direction: column; width: 100%; min-height: 0; - padding: 10vh 0 6vh; overflow: hidden auto; transform: translateY(-2rem); - - @media screen and (orientation: portrait) { - padding: 5vh 0; - } } diff --git a/src/renderer/features/lyrics/unsynchronized-lyrics.tsx b/src/renderer/features/lyrics/unsynchronized-lyrics.tsx index 6ee2597c5..6b40b4f08 100644 --- a/src/renderer/features/lyrics/unsynchronized-lyrics.tsx +++ b/src/renderer/features/lyrics/unsynchronized-lyrics.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react'; 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 { useLyricsDisplaySettings, useLyricsSettings } from '/@/renderer/store'; import { FullLyricsMetadata } from '/@/shared/types/domain-types'; @@ -49,35 +50,42 @@ export const UnsynchronizedLyrics = ({ }, [romajiLyrics]); return ( -
- {settings.showProvider && source && ( - - )} - {settings.showMatch && remote && ( - - )} - {lines.map((text, idx) => ( - - ))} +
+ + {settings.showProvider && source && ( + + )} + {settings.showMatch && remote && ( + + )} + {lines.map((text, idx) => ( + + ))} +
); }; diff --git a/src/renderer/features/player/components/full-screen-player.tsx b/src/renderer/features/player/components/full-screen-player.tsx index d4196aa03..b89444e87 100644 --- a/src/renderer/features/player/components/full-screen-player.tsx +++ b/src/renderer/features/player/components/full-screen-player.tsx @@ -248,7 +248,14 @@ const Controls = () => { }; const handleLyricsSettings = (property: string, value: any) => { - const displayProperties = ['fontSize', 'fontSizeUnsync', 'gap', 'gapUnsync']; + const displayProperties = [ + 'fontSize', + 'fontSizeUnsync', + 'gap', + 'gapUnsync', + 'paddingLeft', + 'paddingRight', + ]; if (displayProperties.includes(property)) { const currentDisplay = useSettingsStore.getState().lyricsDisplay; setSettings({ @@ -475,6 +482,38 @@ const Controls = () => { + + + +