diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 1b3556d0a..c23b584b5 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -473,6 +473,7 @@ "lyricFetchProvider_description": "select the providers to fetch lyrics from. the order of the providers is the order in which they will be queried", "lyricOffset": "lyric offset (ms)", "lyricOffset_description": "offset the lyric by the specified amount of milliseconds", + "lyricWdith": "lyric overlay width", "minimizeToTray": "minimize to tray", "minimizeToTray_description": "minimize the application to the system tray", "minimumScrobblePercentage": "minimum scrobble duration (percentage)", diff --git a/src/renderer/features/lyrics/lyrics-actions.tsx b/src/renderer/features/lyrics/lyrics-actions.tsx index 3ce52b13e..769dc9e78 100644 --- a/src/renderer/features/lyrics/lyrics-actions.tsx +++ b/src/renderer/features/lyrics/lyrics-actions.tsx @@ -1,4 +1,4 @@ -import { Box, Group } from '@mantine/core'; +import { Box, Grid, Group } from '@mantine/core'; import isElectron from 'is-electron'; import { useTranslation } from 'react-i18next'; import { RiAddFill, RiSubtractFill } from 'react-icons/ri'; @@ -6,11 +6,14 @@ import { LyricsOverride } from '/@/renderer/api/types'; import { Button, NumberInput, Tooltip } from '/@/renderer/components'; import { openLyricSearchModal } from '/@/renderer/features/lyrics/components/lyrics-search-form'; import { + useAppStoreActions, useCurrentSong, useLyricsSettings, + useLyricsStore, useSettingsStore, useSettingsStoreActions, } from '/@/renderer/store'; +import { useCallback } from 'react'; interface LyricsActionsProps { onRemoveLyric: () => void; @@ -26,7 +29,9 @@ export const LyricsActions = ({ const { t } = useTranslation(); const currentSong = useCurrentSong(); const { setSettings } = useSettingsStoreActions(); + const { setLyrics } = useAppStoreActions(); const { delayMs, sources } = useLyricsSettings(); + const { open, width } = useLyricsStore(); const handleLyricOffset = (e: number) => { setSettings({ @@ -37,79 +42,120 @@ export const LyricsActions = ({ }); }; + const setWidth = useCallback( + (newWidth: number) => { + setLyrics({ open, width: newWidth }); + }, + [open, setLyrics], + ); + const isActionsDisabled = !currentSong; const isDesktop = isElectron(); return ( - + + {true && ( + + + setWidth(Number(e.currentTarget.value))} + /> + + + )} {isDesktop && sources.length ? ( - + + + ) : null} - - - - - + + + + + + + + + + {isDesktop && sources.length ? ( - + + + ) : null} - - {isDesktop && sources.length ? ( - + + + ) : null} - + ); }; diff --git a/src/renderer/features/lyrics/synchronized-lyrics.tsx b/src/renderer/features/lyrics/synchronized-lyrics.tsx index 061d18889..3a5f0db9a 100644 --- a/src/renderer/features/lyrics/synchronized-lyrics.tsx +++ b/src/renderer/features/lyrics/synchronized-lyrics.tsx @@ -165,7 +165,12 @@ export const SynchronizedLyrics = ({ currentLyric.classList.add('active'); if (followRef.current) { - doc?.scroll({ behavior: 'smooth', top: offsetTop }); + // Have smooth scrolling for line-by-line, instant for large jumps + const behavior = + Math.abs(doc.scrollTop - offsetTop) > 8 * settings.fontSize + ? 'instant' + : 'smooth'; + doc?.scroll({ behavior, top: offsetTop }); } if (index !== lyricRef.current!.length - 1) { @@ -178,7 +183,7 @@ export const SynchronizedLyrics = ({ }, nextTime - timeInMs - elapsed); } }, - [], + [settings.fontSize], ); useEffect(() => { diff --git a/src/renderer/layouts/default-layout/lyrics-overlay.tsx b/src/renderer/layouts/default-layout/lyrics-overlay.tsx index e810fffd9..5c3884d9e 100644 --- a/src/renderer/layouts/default-layout/lyrics-overlay.tsx +++ b/src/renderer/layouts/default-layout/lyrics-overlay.tsx @@ -1,5 +1,5 @@ -import { AnimatePresence, Variants, motion, useDragControls } from 'framer-motion'; -import { useMemo, useRef } from 'react'; +import { useMemo, useRef, useState } from 'react'; +import { Variants, motion, useDragControls } from 'framer-motion'; import { Lyrics } from '/@/renderer/features/lyrics/lyrics'; import styled from 'styled-components'; import { Platform } from '/@/renderer/types'; @@ -21,6 +21,9 @@ export const LyricsOverlay = () => { const dragControls = useDragControls(); const constraintsRef = useRef(null); const { open, width } = useLyricsStore(); + const lyricRef = useRef(null); + + const [x, setX] = useState((document.body.clientWidth - width) / 2); const variants: Variants = useMemo( () => ({ @@ -29,7 +32,6 @@ export const LyricsOverlay = () => { windowBarStyle === Platform.WINDOWS || Platform.MACOS ? 'calc(100vh - 205px)' : 'calc(100vh - 175px)', - left: 'calc(50vw - 225px)', position: 'absolute', top: '75px', transition: { @@ -37,6 +39,7 @@ export const LyricsOverlay = () => { ease: 'anticipate', }, width, + x, }), open: (windowBarStyle) => ({ boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.8)', @@ -45,47 +48,45 @@ export const LyricsOverlay = () => { windowBarStyle === Platform.WINDOWS || Platform.MACOS ? 'calc(100vh - 205px)' : 'calc(100vh - 175px)', - transition: { - damping: 10, - delay: 0, - duration: 0.4, - ease: 'anticipate', - mass: 0.5, - }, width, + x, zIndex: 121, }), }), - [width], + [width, x], ); return ( - - {open && ( - - - - - - )} - + open && ( + + { + // bodge to save the current position, so that on resize + // window does not snap back to 0 + setX(lyricRef.current!.getBoundingClientRect().x); + }} + > + + + + ) ); }; diff --git a/src/renderer/store/app.store.ts b/src/renderer/store/app.store.ts index 428ea50ae..b0c84c343 100644 --- a/src/renderer/store/app.store.ts +++ b/src/renderer/store/app.store.ts @@ -93,7 +93,7 @@ export const useAppStore = create()( isReorderingQueue: false, lyrics: { open: false, - width: 450, + width: 525, }, platform: Platform.WINDOWS, sidebar: {