mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c5671cf38 | |||
| bd12fbecac | |||
| c1d88ada91 | |||
| d6a3e1d90b | |||
| 789c7f3d81 | |||
| f3c785d0fa | |||
| 062c1c2b61 | |||
| eb078d62cd | |||
| c429ac9223 | |||
| bd26967ff2 | |||
| 620b810191 | |||
| 64866c59bd | |||
| 0afbe4c0a2 | |||
| 6782cd0dcc | |||
| 8f585a5be9 | |||
| ac0c396712 | |||
| b989a66991 | |||
| 2814b623e7 |
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "feishin",
|
||||
"version": "0.15.1",
|
||||
"version": "0.16.0",
|
||||
"description": "A modern self-hosted music player.",
|
||||
"keywords": [
|
||||
"subsonic",
|
||||
|
||||
@@ -113,7 +113,9 @@
|
||||
"trackPeak": "Track-Spitzenpegel",
|
||||
"codec": "Codec",
|
||||
"albumPeak": "Album-Spitzenpegel",
|
||||
"albumGain": "Album-Pegelverstärkung"
|
||||
"albumGain": "Album-Pegelverstärkung",
|
||||
"tags": "tags",
|
||||
"viewReleaseNotes": "Release Notes anzeigen"
|
||||
},
|
||||
"error": {
|
||||
"remotePortWarning": "Starten Sie den Server neu, um den neuen Port anzuwenden",
|
||||
@@ -237,7 +239,8 @@
|
||||
"description": "Beschreibung",
|
||||
"setExpiration": "Ablaufdatum setzen",
|
||||
"expireInvalid": "Ablaufdatum muss in der Zukunft liegen",
|
||||
"allowDownloading": "Herunterladen zulassen"
|
||||
"allowDownloading": "Herunterladen zulassen",
|
||||
"success": "Link in die Zwischenablage kopiert (oder hier klicken um zu öffnen)"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -429,7 +432,8 @@
|
||||
"home": "$t(common.home)",
|
||||
"artists": "$t(entity.artist_other)",
|
||||
"albumArtists": "$t(entity.albumArtist_other)",
|
||||
"shared": "$t(entity.playlist_other) geteilt"
|
||||
"shared": "$t(entity.playlist_other) geteilt",
|
||||
"myLibrary": "meine bibliothek"
|
||||
},
|
||||
"setting": {
|
||||
"playbackTab": "Wiedergabe",
|
||||
@@ -676,6 +680,17 @@
|
||||
"clearCache_description": "Hartes Zurücksetzen. Neben feishins Zwischenspeicher wird auch der des Browsers gelöscht (Bilder und andere Daten). Zugangsinformationen und Einstellungen werden behalten",
|
||||
"sidePlayQueueStyle": "Wiedergabelistenstil in der Seitenleiste",
|
||||
"zoom_description": "Setzt den Zoom (in %) für das Programm",
|
||||
"zoom": "Zoom"
|
||||
"zoom": "Zoom",
|
||||
"albumBackground": "Album Hintergrund",
|
||||
"customCss": "Benutzerdefiniert css",
|
||||
"homeConfiguration": "Startseite Konfiguration",
|
||||
"lastfmApiKey": "{{lastfm}} API-Schlüssel",
|
||||
"lastfmApiKey_description": "Der API-Schlüssel für {{lastfm}}. wird für benötigt",
|
||||
"discordListening": "Status als hört zu anzeigen",
|
||||
"discordListening_description": "Status als hört zu statt als spielt anzeigen",
|
||||
"lastfm": "zeige last.fm links",
|
||||
"lastfm_description": "zeige links zu last.fm auf dem Künstler/Album-Seiten",
|
||||
"musicbrainz": "Zeig musicbrainz links",
|
||||
"customCssEnable": "aktiviere Benutzerdefinierte css"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,6 +536,8 @@
|
||||
"floatingQueueArea_description": "display a hover icon on the right side of the screen to view the play queue",
|
||||
"followLyric": "follow current lyric",
|
||||
"followLyric_description": "scroll the lyric to the current playing position",
|
||||
"preferLocalLyrics": "prefer local lyrics",
|
||||
"preferLocalLyrics_description": "prefer local lyrics over remote lyrics when available",
|
||||
"font": "font",
|
||||
"font_description": "sets the font to use for the application",
|
||||
"fontType": "font type",
|
||||
|
||||
@@ -385,7 +385,9 @@
|
||||
"preview": "Vista previa",
|
||||
"translation": "traducción",
|
||||
"additionalParticipants": "Participantes adicionales",
|
||||
"tags": "Etiquetas"
|
||||
"tags": "Etiquetas",
|
||||
"newVersion": "Una nueva versión ha sido instalada ({{version}})",
|
||||
"viewReleaseNotes": "Ver notas de lanzamiento"
|
||||
},
|
||||
"error": {
|
||||
"remotePortWarning": "reiniciar el servidor para aplicar el nuevo puerto",
|
||||
@@ -469,7 +471,8 @@
|
||||
"home": "$t(common.home)",
|
||||
"artists": "$t(entity.artist_other)",
|
||||
"albumArtists": "$t(entity.albumArtist_other)",
|
||||
"shared": "compartido $t(entity.playlist_other)"
|
||||
"shared": "compartido $t(entity.playlist_other)",
|
||||
"myLibrary": "Mi biblioteca"
|
||||
},
|
||||
"appMenu": {
|
||||
"selectServer": "seleccionar servidor",
|
||||
@@ -655,7 +658,8 @@
|
||||
},
|
||||
"queryEditor": {
|
||||
"input_optionMatchAll": "coincidir todos",
|
||||
"input_optionMatchAny": "coincidir cualquiera"
|
||||
"input_optionMatchAny": "coincidir cualquiera",
|
||||
"title": "Editor de consultas"
|
||||
},
|
||||
"shareItem": {
|
||||
"createFailed": "No se pudo crear el recurso compartido (¿está habilitado el uso compartido?)",
|
||||
@@ -737,7 +741,9 @@
|
||||
"view": {
|
||||
"card": "tarjeta",
|
||||
"table": "tabla",
|
||||
"poster": "cartel"
|
||||
"poster": "cartel",
|
||||
"list": "Lista",
|
||||
"grid": "Cuadrícula"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -90,7 +90,9 @@
|
||||
"trackGain": "raidan vahvistus (gain)",
|
||||
"trackPeak": "kappaleen huippu (peak)",
|
||||
"additionalParticipants": "muut osallistujat",
|
||||
"tags": "tägit"
|
||||
"tags": "tägit",
|
||||
"newVersion": "uusi versio on asennettu ({{version}})",
|
||||
"viewReleaseNotes": "katsele julkaisutietoja"
|
||||
},
|
||||
"entity": {
|
||||
"album_one": "albumi",
|
||||
@@ -279,7 +281,8 @@
|
||||
},
|
||||
"queryEditor": {
|
||||
"input_optionMatchAny": "sovita joku",
|
||||
"input_optionMatchAll": "sovita kaikki"
|
||||
"input_optionMatchAll": "sovita kaikki",
|
||||
"title": "kyselyeditori"
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
@@ -515,7 +518,9 @@
|
||||
"lastfm_description": "näytä linkit last.fm sivulle artistin/albumin sivuilla",
|
||||
"musicbrainz": "näytä musicbrainz linkit",
|
||||
"neteaseTranslation": "Ota NetEasen käännökset käyttöön",
|
||||
"neteaseTranslation_description": "Käytöss ollessa noutaa ja näyttää käännetyt sanat NetEasesta, jos ne ovat saatavilla."
|
||||
"neteaseTranslation_description": "Käytöss ollessa noutaa ja näyttää käännetyt sanat NetEasesta, jos ne ovat saatavilla.",
|
||||
"preferLocalLyrics_description": "suosi paikallisia sanoituksia ulkoisten sijasta, kun saatavilla",
|
||||
"preferLocalLyrics": "suosi paikallisia sanoituksia"
|
||||
},
|
||||
"page": {
|
||||
"itemDetail": {
|
||||
@@ -584,7 +589,8 @@
|
||||
"home": "$t(common.home)",
|
||||
"nowPlaying": "nyt soi",
|
||||
"playlists": "$t(entity.playlist_other)",
|
||||
"search": "$t(common.search)"
|
||||
"search": "$t(common.search)",
|
||||
"myLibrary": "oma kirjasto"
|
||||
},
|
||||
"setting": {
|
||||
"generalTab": "yleinen",
|
||||
@@ -745,7 +751,9 @@
|
||||
"view": {
|
||||
"table": "taulukko",
|
||||
"card": "kortti",
|
||||
"poster": "juliste"
|
||||
"poster": "juliste",
|
||||
"grid": "ruudukko",
|
||||
"list": "lista"
|
||||
}
|
||||
},
|
||||
"column": {
|
||||
|
||||
@@ -150,7 +150,9 @@
|
||||
"codec": "codec",
|
||||
"translation": "traduction",
|
||||
"additionalParticipants": "participants additionnels",
|
||||
"tags": "tags"
|
||||
"tags": "tags",
|
||||
"newVersion": "une nouvelle version vient d'être installé ({{version}})",
|
||||
"viewReleaseNotes": "voir la note de version"
|
||||
},
|
||||
"error": {
|
||||
"remotePortWarning": "redémarrer le serveur pour appliquer le nouveau port",
|
||||
@@ -234,7 +236,8 @@
|
||||
"home": "$t(common.home)",
|
||||
"artists": "$t(entity.artist_other)",
|
||||
"albumArtists": "$t(entity.albumArtist_other)",
|
||||
"shared": "partagé $t(entity.playlist_other)"
|
||||
"shared": "partagé $t(entity.playlist_other)",
|
||||
"myLibrary": "ma bibliothèque"
|
||||
},
|
||||
"fullscreenPlayer": {
|
||||
"config": {
|
||||
@@ -602,7 +605,9 @@
|
||||
"lastfm": "affiche les liens de last.fm",
|
||||
"musicbrainz_description": "affiches les liens vers musicbrainz sur les pages des artistes/albums, quand mbid existes",
|
||||
"lastfm_description": "affiche les liens vers last.fm sur les pages des artistes/albums",
|
||||
"musicbrainz": "affiches les liens musicbrainz"
|
||||
"musicbrainz": "affiches les liens musicbrainz",
|
||||
"neteaseTranslation": "Activer les traductions NetEase",
|
||||
"neteaseTranslation_description": "Lorsque cette option est activée, récupère et affiche les paroles traduites de NetEase si elles sont disponibles."
|
||||
},
|
||||
"form": {
|
||||
"deletePlaylist": {
|
||||
@@ -643,7 +648,8 @@
|
||||
},
|
||||
"queryEditor": {
|
||||
"input_optionMatchAll": "correspondre à tous",
|
||||
"input_optionMatchAny": "correspondre à n'importe quel"
|
||||
"input_optionMatchAny": "correspondre à n'importe quel",
|
||||
"title": "éditeur de requête"
|
||||
},
|
||||
"editPlaylist": {
|
||||
"title": "modifier $t(entity.playlist_one)",
|
||||
@@ -733,7 +739,9 @@
|
||||
"view": {
|
||||
"table": "liste",
|
||||
"poster": "poster",
|
||||
"card": "Carte"
|
||||
"card": "Carte",
|
||||
"grid": "grille",
|
||||
"list": "liste"
|
||||
},
|
||||
"label": {
|
||||
"releaseDate": "date de sortie",
|
||||
|
||||
@@ -111,7 +111,9 @@
|
||||
"preview": "预览",
|
||||
"translation": "翻译",
|
||||
"additionalParticipants": "其他参与者",
|
||||
"tags": "标签"
|
||||
"tags": "标签",
|
||||
"viewReleaseNotes": "查看发行说明",
|
||||
"newVersion": "已安装新版本 ({{version}})"
|
||||
},
|
||||
"entity": {
|
||||
"albumArtist_other": "专辑艺术家",
|
||||
@@ -483,7 +485,8 @@
|
||||
"home": "$t(common.home)",
|
||||
"artists": "$t(entity.artist_other)",
|
||||
"albumArtists": "$t(entity.albumArtist_other)",
|
||||
"shared": "共享$t(entity.playlist_other)"
|
||||
"shared": "共享$t(entity.playlist_other)",
|
||||
"myLibrary": "我的媒体库"
|
||||
},
|
||||
"fullscreenPlayer": {
|
||||
"config": {
|
||||
@@ -659,7 +662,8 @@
|
||||
},
|
||||
"queryEditor": {
|
||||
"input_optionMatchAll": "匹配全部",
|
||||
"input_optionMatchAny": "匹配任何"
|
||||
"input_optionMatchAny": "匹配任何",
|
||||
"title": "查询编辑器"
|
||||
},
|
||||
"editPlaylist": {
|
||||
"title": "编辑$t(entity.playlist_one)",
|
||||
@@ -695,7 +699,9 @@
|
||||
"view": {
|
||||
"table": "表格",
|
||||
"poster": "海报",
|
||||
"card": "卡片"
|
||||
"card": "卡片",
|
||||
"grid": "网格",
|
||||
"list": "列表"
|
||||
},
|
||||
"label": {
|
||||
"releaseDate": "发布日期",
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
.lyric-line {
|
||||
padding: 0 1rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
color: var(--theme-colors-foreground);
|
||||
word-break: keep-all;
|
||||
opacity: 0.5;
|
||||
transition:
|
||||
opacity 0.3s ease-in-out,
|
||||
|
||||
@@ -86,7 +86,7 @@ export const useSongLyricsBySong = (
|
||||
song: QueueSong | undefined,
|
||||
): UseQueryResult<FullLyricsMetadata | StructuredLyric[]> => {
|
||||
const { query } = args;
|
||||
const { fetch } = useLyricsSettings();
|
||||
const { fetch, preferLocalLyrics } = useLyricsSettings();
|
||||
const server = getServerById(song?.serverId);
|
||||
|
||||
return useQuery({
|
||||
@@ -97,6 +97,9 @@ export const useSongLyricsBySong = (
|
||||
if (!server) throw new Error('Server not found');
|
||||
if (!song) return null;
|
||||
|
||||
let localLyrics: FullLyricsMetadata | null | StructuredLyric[] = null;
|
||||
let remoteLyrics: FullLyricsMetadata | null | StructuredLyric[] = null;
|
||||
|
||||
if (hasFeature(server, ServerFeature.LYRICS_MULTIPLE_STRUCTURED)) {
|
||||
const subsonicLyrics = await api.controller
|
||||
.getStructuredLyrics({
|
||||
@@ -106,7 +109,7 @@ export const useSongLyricsBySong = (
|
||||
.catch(console.error);
|
||||
|
||||
if (subsonicLyrics?.length) {
|
||||
return subsonicLyrics;
|
||||
localLyrics = subsonicLyrics;
|
||||
}
|
||||
} else if (hasFeature(server, ServerFeature.LYRICS_SINGLE_STRUCTURED)) {
|
||||
const jfLyrics = await api.controller
|
||||
@@ -117,7 +120,7 @@ export const useSongLyricsBySong = (
|
||||
.catch((err) => console.log(err));
|
||||
|
||||
if (jfLyrics) {
|
||||
return {
|
||||
localLyrics = {
|
||||
artist: song.artists?.[0]?.name,
|
||||
lyrics: jfLyrics,
|
||||
name: song.name,
|
||||
@@ -126,7 +129,7 @@ export const useSongLyricsBySong = (
|
||||
};
|
||||
}
|
||||
} else if (song.lyrics) {
|
||||
return {
|
||||
localLyrics = {
|
||||
artist: song.artists?.[0]?.name,
|
||||
lyrics: formatLyrics(song.lyrics),
|
||||
name: song.name,
|
||||
@@ -135,12 +138,16 @@ export const useSongLyricsBySong = (
|
||||
};
|
||||
}
|
||||
|
||||
if (preferLocalLyrics && localLyrics) {
|
||||
return localLyrics;
|
||||
}
|
||||
|
||||
if (fetch) {
|
||||
const remoteLyricsResult: InternetProviderLyricResponse | null =
|
||||
await lyricsIpc?.getRemoteLyricsBySong(song);
|
||||
|
||||
if (remoteLyricsResult) {
|
||||
return {
|
||||
remoteLyrics = {
|
||||
...remoteLyricsResult,
|
||||
lyrics: formatLyrics(remoteLyricsResult.lyrics),
|
||||
remote: true,
|
||||
@@ -148,6 +155,14 @@ export const useSongLyricsBySong = (
|
||||
}
|
||||
}
|
||||
|
||||
if (remoteLyrics) {
|
||||
return remoteLyrics;
|
||||
}
|
||||
|
||||
if (localLyrics) {
|
||||
return localLyrics;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
queryKey: queryKeys.songs.lyrics(server?.id || '', query),
|
||||
@@ -183,9 +198,7 @@ export const useSongLyricsByRemoteId = (
|
||||
);
|
||||
},
|
||||
queryFn: async () => {
|
||||
const remoteLyricsResult = await lyricsIpc?.getRemoteLyricsByRemoteId(
|
||||
query as LyricGetQuery,
|
||||
);
|
||||
const remoteLyricsResult = await lyricsIpc?.getRemoteLyricsByRemoteId(query as any);
|
||||
|
||||
if (remoteLyricsResult) {
|
||||
return formatLyrics(remoteLyricsResult);
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
object-fit: var(--theme-image-fit);
|
||||
object-position: 50% 100%;
|
||||
border-radius: 5px;
|
||||
filter: drop-shadow(0 0 5px rgb(0 0 0 / 40%)) drop-shadow(0 0 5px rgb(0 0 0 / 40%));
|
||||
}
|
||||
@@ -24,8 +22,13 @@
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
border-radius: 5px;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.5vh;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ import { Center } from '/@/shared/components/center/center';
|
||||
import { Flex } from '/@/shared/components/flex/flex';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Image } from '/@/shared/components/image/image';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { TextTitle } from '/@/shared/components/text-title/text-title';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { PlayerData, QueueSong } from '/@/shared/types/domain-types';
|
||||
|
||||
@@ -52,9 +50,14 @@ const scaleImageUrl = (imageSize: number, url?: null | string) => {
|
||||
.replace(/&height=\d+/, `&height=${imageSize}`);
|
||||
};
|
||||
|
||||
const MotionImage = motion.create(Image);
|
||||
const MotionImage = motion.img;
|
||||
|
||||
const ImageWithPlaceholder = ({
|
||||
className,
|
||||
...props
|
||||
}: HTMLMotionProps<'img'> & { placeholder?: string }) => {
|
||||
const nativeAspectRatio = useSettingsStore((store) => store.general.nativeAspectRatio);
|
||||
|
||||
const ImageWithPlaceholder = ({ ...props }: HTMLMotionProps<'img'> & { placeholder?: string }) => {
|
||||
if (!props.src) {
|
||||
return (
|
||||
<Center
|
||||
@@ -76,7 +79,11 @@ const ImageWithPlaceholder = ({ ...props }: HTMLMotionProps<'img'> & { placehold
|
||||
|
||||
return (
|
||||
<MotionImage
|
||||
className={styles.image}
|
||||
className={clsx(styles.image, className)}
|
||||
style={{
|
||||
objectFit: nativeAspectRatio ? 'contain' : 'cover',
|
||||
width: nativeAspectRatio ? 'auto' : '100%',
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -201,45 +208,35 @@ export const FullScreenPlayerImage = () => {
|
||||
</div>
|
||||
<Stack
|
||||
className={styles.metadataContainer}
|
||||
gap="xs"
|
||||
gap="md"
|
||||
maw="100%"
|
||||
>
|
||||
<TextTitle
|
||||
<Text
|
||||
fw={900}
|
||||
order={1}
|
||||
lh="1.2"
|
||||
overflow="hidden"
|
||||
size="4xl"
|
||||
w="100%"
|
||||
>
|
||||
{currentSong?.name}
|
||||
</TextTitle>
|
||||
<TextTitle
|
||||
</Text>
|
||||
<Text
|
||||
component={Link}
|
||||
fw={600}
|
||||
isLink
|
||||
order={3}
|
||||
overflow="hidden"
|
||||
style={{
|
||||
textShadow: 'var(--theme-fullscreen-player-text-shadow)',
|
||||
}}
|
||||
size="xl"
|
||||
to={generatePath(AppRoute.LIBRARY_ALBUMS_DETAIL, {
|
||||
albumId: currentSong?.albumId || '',
|
||||
})}
|
||||
w="100%"
|
||||
>
|
||||
{currentSong?.album}{' '}
|
||||
</TextTitle>
|
||||
<TextTitle
|
||||
key="fs-artists"
|
||||
order={3}
|
||||
style={{
|
||||
textShadow: 'var(--theme-fullscreen-player-text-shadow)',
|
||||
}}
|
||||
>
|
||||
{currentSong?.album}
|
||||
</Text>
|
||||
<Text key="fs-artists">
|
||||
{currentSong?.artists?.map((artist, index) => (
|
||||
<Fragment key={`fs-artist-${artist.id}`}>
|
||||
{index > 0 && (
|
||||
<Text
|
||||
isMuted
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '0 0.5rem',
|
||||
@@ -250,12 +247,7 @@ export const FullScreenPlayerImage = () => {
|
||||
)}
|
||||
<Text
|
||||
component={Link}
|
||||
fw={600}
|
||||
isLink
|
||||
isMuted
|
||||
style={{
|
||||
textShadow: 'var(--theme-fullscreen-player-text-shadow)',
|
||||
}}
|
||||
to={generatePath(AppRoute.LIBRARY_ALBUM_ARTISTS_DETAIL, {
|
||||
albumArtistId: artist.id,
|
||||
})}
|
||||
@@ -264,7 +256,7 @@ export const FullScreenPlayerImage = () => {
|
||||
</Text>
|
||||
</Fragment>
|
||||
))}
|
||||
</TextTitle>
|
||||
</Text>
|
||||
<Group
|
||||
justify="center"
|
||||
mt="sm"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useHotkeys } from '@mantine/hooks';
|
||||
import { motion, Variants } from 'motion/react';
|
||||
import { CSSProperties, useLayoutEffect, useRef } from 'react';
|
||||
import { CSSProperties, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocation } from 'react-router';
|
||||
|
||||
@@ -32,7 +32,11 @@ import { Platform } from '/@/shared/types/types';
|
||||
|
||||
const mainBackground = 'var(--theme-colors-background)';
|
||||
|
||||
const Controls = () => {
|
||||
interface ControlsProps {
|
||||
isPageHovered: boolean;
|
||||
}
|
||||
|
||||
const Controls = ({ isPageHovered }: ControlsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
dynamicBackground,
|
||||
@@ -77,7 +81,7 @@ const Controls = () => {
|
||||
iconProps={{ size: 'lg' }}
|
||||
onClick={handleToggleFullScreenPlayer}
|
||||
tooltip={{ label: t('common.minimize', { postProcess: 'titleCase' }) }}
|
||||
variant="subtle"
|
||||
variant={isPageHovered ? 'default' : 'subtle'}
|
||||
/>
|
||||
<Popover position="bottom-start">
|
||||
<Popover.Target>
|
||||
@@ -85,7 +89,7 @@ const Controls = () => {
|
||||
icon="settings"
|
||||
iconProps={{ size: 'lg' }}
|
||||
tooltip={{ label: t('common.configure', { postProcess: 'titleCase' }) }}
|
||||
variant="subtle"
|
||||
variant={isPageHovered ? 'default' : 'subtle'}
|
||||
/>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
@@ -410,6 +414,8 @@ export const FullScreenPlayer = () => {
|
||||
const { setStore } = useFullScreenPlayerStoreActions();
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
|
||||
const [isPageHovered, setIsPageHovered] = useState(false);
|
||||
|
||||
const location = useLocation();
|
||||
const isOpenedRef = useRef<boolean | null>(null);
|
||||
|
||||
@@ -441,10 +447,12 @@ export const FullScreenPlayer = () => {
|
||||
custom={{ background, backgroundImage, dynamicBackground, windowBarStyle }}
|
||||
exit="closed"
|
||||
initial="closed"
|
||||
onMouseEnter={() => setIsPageHovered(true)}
|
||||
onMouseLeave={() => setIsPageHovered(false)}
|
||||
transition={{ duration: 2 }}
|
||||
variants={containerVariants}
|
||||
>
|
||||
<Controls />
|
||||
<Controls isPageHovered={isPageHovered} />
|
||||
{dynamicBackground && (
|
||||
<div
|
||||
className={styles.backgroundImageOverlay}
|
||||
|
||||
@@ -71,7 +71,7 @@ export const LeftControls = () => {
|
||||
<LayoutGroup>
|
||||
<AnimatePresence
|
||||
initial={false}
|
||||
mode="wait"
|
||||
mode="popLayout"
|
||||
>
|
||||
{!hideImage && (
|
||||
<div className={styles.imageWrapper}>
|
||||
@@ -83,7 +83,7 @@ export const LeftControls = () => {
|
||||
key="playerbar-image"
|
||||
onClick={handleToggleFullScreenPlayer}
|
||||
role="button"
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
transition={{ duration: 0.2, ease: 'easeIn' }}
|
||||
>
|
||||
<Tooltip
|
||||
label={t('player.toggleFullscreenPlayer', {
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
}
|
||||
|
||||
.main {
|
||||
background: var(--theme-colors-foreground) !important;
|
||||
border-radius: 50%;
|
||||
|
||||
svg {
|
||||
|
||||
@@ -15,7 +15,7 @@ interface PlayerButtonProps extends Omit<ActionIconProps, 'icon' | 'variant'> {
|
||||
}
|
||||
|
||||
export const PlayerButton = forwardRef<HTMLButtonElement, PlayerButtonProps>(
|
||||
({ icon, isActive, tooltip, variant, ...rest }: PlayerButtonProps) => {
|
||||
({ icon, isActive, tooltip, variant, ...rest }: PlayerButtonProps, ref) => {
|
||||
if (tooltip) {
|
||||
return (
|
||||
<Tooltip {...tooltip}>
|
||||
@@ -23,6 +23,7 @@ export const PlayerButton = forwardRef<HTMLButtonElement, PlayerButtonProps>(
|
||||
className={clsx({
|
||||
[styles.active]: isActive,
|
||||
})}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -41,6 +42,7 @@ export const PlayerButton = forwardRef<HTMLButtonElement, PlayerButtonProps>(
|
||||
className={clsx(styles.playerButton, styles[variant], {
|
||||
[styles.active]: isActive,
|
||||
})}
|
||||
ref={ref}
|
||||
{...rest}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -58,21 +60,23 @@ interface PlayButtonProps extends Omit<ActionIconProps, 'icon' | 'variant'> {
|
||||
isPaused?: boolean;
|
||||
}
|
||||
|
||||
export const PlayButton = ({ isPaused, ...props }: PlayButtonProps) => {
|
||||
return (
|
||||
<ActionIcon
|
||||
className={styles.main}
|
||||
icon={isPaused ? 'mediaPlay' : 'mediaPause'}
|
||||
iconProps={{
|
||||
size: 'lg',
|
||||
}}
|
||||
tooltip={
|
||||
isPaused
|
||||
? t('player.play', { postProcess: 'sentenceCase' })
|
||||
: t('player.pause', { postProcess: 'sentenceCase' })
|
||||
}
|
||||
variant="white"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const PlayButton = forwardRef<HTMLButtonElement, PlayButtonProps>(
|
||||
({ isPaused, ...props }: PlayButtonProps, ref) => {
|
||||
return (
|
||||
<ActionIcon
|
||||
className={styles.main}
|
||||
icon={isPaused ? 'mediaPlay' : 'mediaPause'}
|
||||
iconProps={{
|
||||
size: 'lg',
|
||||
}}
|
||||
ref={ref}
|
||||
tooltip={{
|
||||
label: isPaused
|
||||
? (t('player.play', { postProcess: 'sentenceCase' }) as string)
|
||||
: (t('player.pause', { postProcess: 'sentenceCase' }) as string),
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -43,6 +43,28 @@ export const LyricSettings = () => {
|
||||
}),
|
||||
title: t('setting.followLyric', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label="Prefer local lyrics"
|
||||
defaultChecked={settings.preferLocalLyrics}
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
lyrics: {
|
||||
...settings,
|
||||
preferLocalLyrics: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: t('setting.preferLocalLyrics', {
|
||||
context: 'description',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.preferLocalLyrics', { postProcess: 'sentenceCase' }),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
|
||||
@@ -14,8 +14,7 @@ export const PlayButton = ({ className, ...props }: PlayButtonProps) => {
|
||||
className={clsx(styles.button, className)}
|
||||
icon="mediaPlay"
|
||||
iconProps={{
|
||||
fill: 'default',
|
||||
size: 'lg',
|
||||
size: 'xl',
|
||||
}}
|
||||
variant="filled"
|
||||
{...props}
|
||||
|
||||
@@ -181,7 +181,9 @@ export const SidebarPlaylistList = () => {
|
||||
const owned: Array<[boolean, () => void] | Playlist> = [];
|
||||
|
||||
for (const playlist of data.items) {
|
||||
owned.push(playlist);
|
||||
if (!playlist.owner || playlist.owner === server.username) {
|
||||
owned.push(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
return { ...base, items: owned };
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
.image-container {
|
||||
position: relative;
|
||||
width: var(--sidebar-image-height);
|
||||
height: var(--sidebar-image-height);
|
||||
cursor: pointer;
|
||||
animation: fade-in 0.2s ease-in-out;
|
||||
|
||||
@@ -19,13 +19,18 @@ import {
|
||||
useSetFullScreenPlayerStore,
|
||||
useSidebarStore,
|
||||
} from '/@/renderer/store';
|
||||
import { SidebarItemType, useGeneralSettings } from '/@/renderer/store/settings.store';
|
||||
import {
|
||||
SidebarItemType,
|
||||
useGeneralSettings,
|
||||
useWindowSettings,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { Accordion } from '/@/shared/components/accordion/accordion';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
||||
import { Platform } from '/@/shared/types/types';
|
||||
|
||||
export const Sidebar = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -64,6 +69,7 @@ export const Sidebar = () => {
|
||||
};
|
||||
|
||||
const { sidebarItems } = useGeneralSettings();
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
|
||||
const sidebarItemsWithRoute: SidebarItemType[] = useMemo(() => {
|
||||
if (!sidebarItems) return [];
|
||||
@@ -80,6 +86,18 @@ export const Sidebar = () => {
|
||||
return items;
|
||||
}, [sidebarItems, translatedSidebarItemMap]);
|
||||
|
||||
const scrollAreaHeight = useMemo(() => {
|
||||
if (showImage) {
|
||||
if (windowBarStyle === Platform.WINDOWS || windowBarStyle === Platform.MACOS) {
|
||||
return `calc(100% - 105px - ${sidebar.leftWidth})`;
|
||||
}
|
||||
|
||||
return `calc(100% - ${sidebar.leftWidth})`;
|
||||
}
|
||||
|
||||
return '100%';
|
||||
}, [showImage, sidebar.leftWidth, windowBarStyle]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.container}
|
||||
@@ -95,7 +113,7 @@ export const Sidebar = () => {
|
||||
allowDragScroll
|
||||
className={styles.scrollArea}
|
||||
style={{
|
||||
maxHeight: showImage ? `calc(100vh - 90px - ${sidebar.leftWidth})` : '100%',
|
||||
height: scrollAreaHeight,
|
||||
}}
|
||||
>
|
||||
<Accordion
|
||||
|
||||
@@ -266,6 +266,7 @@ export interface SettingsState {
|
||||
fontSizeUnsync: number;
|
||||
gap: number;
|
||||
gapUnsync: number;
|
||||
preferLocalLyrics: boolean;
|
||||
showMatch: boolean;
|
||||
showProvider: boolean;
|
||||
sources: LyricSource[];
|
||||
@@ -448,6 +449,7 @@ const initialState: SettingsState = {
|
||||
fontSizeUnsync: 24,
|
||||
gap: 24,
|
||||
gapUnsync: 24,
|
||||
preferLocalLyrics: true,
|
||||
showMatch: true,
|
||||
showProvider: true,
|
||||
sources: [LyricSource.NETEASE, LyricSource.LRCLIB],
|
||||
|
||||
@@ -50,7 +50,10 @@ export const useAppTheme = (overrideTheme?: AppTheme) => {
|
||||
useEffect(() => {
|
||||
if (type === FontType.SYSTEM && system) {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--theme-content-font-family', 'dynamic-font');
|
||||
root.style.setProperty(
|
||||
'--theme-content-font-family',
|
||||
'dynamic-font, "Noto Sans JP", sans-serif',
|
||||
);
|
||||
|
||||
if (!textStyleRef.current) {
|
||||
textStyleRef.current = document.createElement('style');
|
||||
@@ -64,7 +67,10 @@ export const useAppTheme = (overrideTheme?: AppTheme) => {
|
||||
}`;
|
||||
} else if (type === FontType.CUSTOM && custom) {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--theme-content-font-family', 'dynamic-font');
|
||||
root.style.setProperty(
|
||||
'--theme-content-font-family',
|
||||
'dynamic-font, "Noto Sans JP", sans-serif',
|
||||
);
|
||||
|
||||
if (!textStyleRef.current) {
|
||||
textStyleRef.current = document.createElement('style');
|
||||
@@ -78,7 +84,10 @@ export const useAppTheme = (overrideTheme?: AppTheme) => {
|
||||
}`;
|
||||
} else {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--theme-content-font-family', builtIn);
|
||||
root.style.setProperty(
|
||||
'--theme-content-font-family',
|
||||
`${builtIn}, "Noto Sans JP", sans-serif`,
|
||||
);
|
||||
}
|
||||
}, [builtIn, custom, system, type]);
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@
|
||||
&:focus-visible {
|
||||
background: darken(var(--theme-colors-primary-filled), 10%);
|
||||
}
|
||||
|
||||
svg {
|
||||
color: var(--theme-colors-primary-contrast);
|
||||
fill: var(--theme-colors-primary-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-variant='subtle'] {
|
||||
@@ -60,8 +65,15 @@
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus-visible {
|
||||
background: lighten(var(--theme-colors-surface), 10%);
|
||||
@mixin dark {
|
||||
background: lighten(var(--theme-colors-background), 5%);
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
background: darken(var(--theme-colors-background), 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,13 @@
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus-visible {
|
||||
background-color: lighten(var(--button-bg), 10%);
|
||||
@mixin dark {
|
||||
background-color: lighten(var(--theme-colors-background), 10%);
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
background-color: darken(var(--theme-colors-background), 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,11 +106,11 @@
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: darken(var(--button-bg), 5%);
|
||||
background-color: darken(var(--theme-colors-background), 5%);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
background-color: darken(var(--button-bg), 10%);
|
||||
background-color: darken(var(--theme-colors-background), 10%);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,10 @@
|
||||
fill: var(--theme-colors-foreground);
|
||||
}
|
||||
|
||||
.fill-contrast {
|
||||
fill: var(--theme-colors-primary-contrast);
|
||||
}
|
||||
|
||||
.fill-inherit {
|
||||
fill: inherit;
|
||||
}
|
||||
|
||||
@@ -224,11 +224,21 @@ export const AppIcon = {
|
||||
|
||||
export interface IconProps extends Omit<IconBaseProps, 'color' | 'fill' | 'size'> {
|
||||
animate?: 'pulse' | 'spin';
|
||||
color?: 'default' | 'error' | 'info' | 'inherit' | 'muted' | 'primary' | 'success' | 'warn';
|
||||
fill?: 'default' | 'error' | 'info' | 'inherit' | 'muted' | 'primary' | 'success' | 'warn';
|
||||
color?: IconColor;
|
||||
fill?: IconColor;
|
||||
icon: keyof typeof AppIcon;
|
||||
size?: '2xl' | '3xl' | '4xl' | '5xl' | 'lg' | 'md' | 'sm' | 'xl' | 'xs' | number | string;
|
||||
}
|
||||
type IconColor =
|
||||
| 'contrast'
|
||||
| 'default'
|
||||
| 'error'
|
||||
| 'info'
|
||||
| 'inherit'
|
||||
| 'muted'
|
||||
| 'primary'
|
||||
| 'success'
|
||||
| 'warn';
|
||||
|
||||
export const Icon = forwardRef<HTMLDivElement, IconProps>((props, ref) => {
|
||||
const { animate, className, color, fill, icon, size = 'md' } = props;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import type { ImgHTMLAttributes } from 'react';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import { motion, MotionConfigProps } from 'motion/react';
|
||||
import { type ImgHTMLAttributes } from 'react';
|
||||
import { Img } from 'react-image';
|
||||
|
||||
import styles from './image.module.css';
|
||||
|
||||
import { animationProps } from '/@/shared/components/animations/animation-props';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
|
||||
|
||||
interface ImageContainerProps {
|
||||
interface ImageContainerProps extends MotionConfigProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
enableAnimation?: boolean;
|
||||
}
|
||||
|
||||
interface ImageLoaderProps {
|
||||
@@ -19,6 +21,8 @@ interface ImageLoaderProps {
|
||||
|
||||
interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src'> {
|
||||
containerClassName?: string;
|
||||
enableAnimation?: boolean;
|
||||
imageContainerProps?: Omit<ImageContainerProps, 'children'>;
|
||||
includeLoader?: boolean;
|
||||
includeUnloader?: boolean;
|
||||
src: string | string[] | undefined;
|
||||
@@ -32,6 +36,8 @@ interface ImageUnloaderProps {
|
||||
export function Image({
|
||||
className,
|
||||
containerClassName,
|
||||
enableAnimation,
|
||||
imageContainerProps,
|
||||
includeLoader = true,
|
||||
includeUnloader = true,
|
||||
src,
|
||||
@@ -41,7 +47,13 @@ export function Image({
|
||||
<Img
|
||||
className={clsx(styles.image, className)}
|
||||
container={(children) => (
|
||||
<ImageContainer className={containerClassName}>{children}</ImageContainer>
|
||||
<ImageContainer
|
||||
className={containerClassName}
|
||||
enableAnimation={enableAnimation}
|
||||
{...imageContainerProps}
|
||||
>
|
||||
{children}
|
||||
</ImageContainer>
|
||||
)}
|
||||
loader={
|
||||
includeLoader ? (
|
||||
@@ -50,7 +62,6 @@ export function Image({
|
||||
</ImageContainer>
|
||||
) : null
|
||||
}
|
||||
loading="lazy"
|
||||
src={src}
|
||||
unloader={
|
||||
includeUnloader ? (
|
||||
@@ -66,8 +77,27 @@ export function Image({
|
||||
return <ImageUnloader />;
|
||||
}
|
||||
|
||||
function ImageContainer({ children, className }: ImageContainerProps) {
|
||||
return <div className={clsx(styles.imageContainer, className)}>{children}</div>;
|
||||
function ImageContainer({ children, className, enableAnimation, ...props }: ImageContainerProps) {
|
||||
if (!enableAnimation) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.imageContainer, className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className={clsx(styles.imageContainer, className)}
|
||||
{...animationProps.fadeIn}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function ImageLoader({ className }: ImageLoaderProps) {
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
text-rendering: optimizelegibility;
|
||||
-webkit-tap-highlight-color: rgb(0 0 0 / 0%);
|
||||
text-size-adjust: none;
|
||||
outline: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -122,59 +123,56 @@ button {
|
||||
@font-face {
|
||||
font-family: Archivo;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Archivo-VariableFont_wdth,wght.ttf')
|
||||
format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Archivo-VariableFont_wdth,wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Raleway-VariableFont_wght.ttf') format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Raleway-VariableFont_wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Fredoka;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Fredoka-VariableFont_wdth,wght.ttf')
|
||||
format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Fredoka-VariableFont_wdth,wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'League Spartan';
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/LeagueSpartan-VariableFont_wght.ttf')
|
||||
format('truetype-variations');
|
||||
src: url('../../../assets/fonts/LeagueSpartan-VariableFont_wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Lexend;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Lexend-VariableFont_wght.ttf') format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Lexend-VariableFont_wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Inter;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Inter-VariableFont_slnt,wght.ttf') format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Inter-VariableFont_slnt,wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Sora;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Sora-VariableFont_wght.ttf') format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Sora-VariableFont_wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Work Sans';
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/WorkSans-VariableFont_wght.ttf') format('truetype-variations');
|
||||
src: url('../../../assets/fonts/WorkSans-VariableFont_wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Poppins;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('../../renderer/fonts/Poppins-Regular.ttf') format('truetype');
|
||||
src: url('../../../assets/fonts/Poppins-Regular.ttf');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@@ -182,7 +180,7 @@ button {
|
||||
font-family: Poppins;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: url('../../renderer/fonts/Poppins-SemiBold.ttf') format('truetype');
|
||||
src: url('../../../assets/fonts/Poppins-SemiBold.ttf');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@@ -190,7 +188,7 @@ button {
|
||||
font-family: Poppins;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url('../../renderer/fonts/Poppins-Bold.ttf') format('truetype');
|
||||
src: url('../../../assets/fonts/Poppins-Bold.ttf');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@@ -198,7 +196,7 @@ button {
|
||||
font-family: Poppins;
|
||||
font-style: normal;
|
||||
font-weight: 800;
|
||||
src: url('../../renderer/fonts/Poppins-ExtraBold.ttf') format('truetype');
|
||||
src: url('../../../assets/fonts/Poppins-ExtraBold.ttf');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@@ -206,28 +204,21 @@ button {
|
||||
font-family: Poppins;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
src: url('../../renderer/fonts/Poppins-Black.ttf') format('truetype');
|
||||
src: url('../../../assets/fonts/Poppins-Black.ttf');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Raleway;
|
||||
font-weight: 100 1000;
|
||||
src: url('../../renderer/fonts/Raleway-VariableFont_wght.ttf') format('truetype-variations');
|
||||
src: url('../../../assets/fonts/Raleway-VariableFont_wght.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: DroidSerif;
|
||||
src: url('https://rawgit.com/google/fonts/master/ufl/ubuntumono/UbuntuMono-Italic.ttf')
|
||||
format('truetype');
|
||||
unicode-range: U+000-5FF; /* Latin glyphs */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: DroidSerif;
|
||||
src: url('https://fonts.gstatic.com/ea/notosansjp/v5/NotoSansJP-Regular.woff2')
|
||||
format('truetype');
|
||||
unicode-range: U+3000-9FFF, U+ff??; /* Japanese glyphs */
|
||||
font-family: 'Noto Sans JP';
|
||||
font-weight: 100 900;
|
||||
src: url('../../../assets/fonts/NotoSansJP-VariableFont_wght.ttf');
|
||||
unicode-range: U+3000-9FFF, U+FF00-FFEF; /* Japanese characters */
|
||||
}
|
||||
|
||||
:root {
|
||||
|
||||
@@ -11,8 +11,8 @@ export const defaultLight: AppThemeConfiguration = {
|
||||
'scrollbar-track-background': 'transparent',
|
||||
},
|
||||
colors: {
|
||||
background: 'rgb(255, 255, 255)',
|
||||
'background-alternate': 'rgb(245, 245, 245)',
|
||||
background: 'rgb(235, 235, 235)',
|
||||
'background-alternate': 'rgb(240, 240, 240)',
|
||||
black: 'rgb(0, 0, 0)',
|
||||
foreground: 'rgb(25, 25, 25)',
|
||||
'foreground-muted': 'rgb(80, 80, 80)',
|
||||
@@ -20,7 +20,7 @@ export const defaultLight: AppThemeConfiguration = {
|
||||
'state-info': 'rgb(0, 122, 255)',
|
||||
'state-success': 'rgb(48, 209, 88)',
|
||||
'state-warning': 'rgb(255, 214, 0)',
|
||||
surface: 'rgb(245, 245, 245)',
|
||||
surface: 'rgb(225, 225, 225)',
|
||||
'surface-foreground': 'rgb(0, 0, 0)',
|
||||
white: 'rgb(255, 255, 255)',
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { AppThemeConfiguration } from './app-theme-types';
|
||||
export const defaultTheme: AppThemeConfiguration = {
|
||||
app: {
|
||||
'overlay-header':
|
||||
'linear-gradient(transparent 0%, rgb(0 0 0 / 75%) 100%), var(--theme-background-noise)',
|
||||
'linear-gradient(transparent 0%, rgb(0 0 0 / 85%) 100%), var(--theme-background-noise)',
|
||||
'overlay-subheader':
|
||||
'linear-gradient(180deg, rgb(0 0 0 / 5%) 0%, var(--theme-colors-background) 100%), var(--theme-background-noise)',
|
||||
'root-font-size': '16px',
|
||||
|
||||
Reference in New Issue
Block a user