mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-21 22:15:40 +00:00
feat(themes): warn about animated themes on high-CPU setups (#1020)
* feat(themes): warn about animated themes on high-CPU setups Show a warning icon + tooltip on animated themes (those defining @keyframes) in the store and in Your Themes, on Linux setups where animation is costly — the Nvidia WebKit quirk is active or compositing is forced off. The Nvidia detection already runs once at startup; its result is recorded (theme_animation.rs) and read via a new theme_animation_risk command — no GPU re-probe. Display-only; never shown off Linux. The animated flag comes from the registry (store) or the theme CSS (Your Themes). * docs(themes): note the animated-theme warning PR in the Theme Store entry
This commit is contained in:
+1
-1
@@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Themes — community Theme Store
|
### Themes — community Theme Store
|
||||||
|
|
||||||
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1009](https://github.com/Psychotoxical/psysonic/pull/1009), [#1011](https://github.com/Psychotoxical/psysonic/pull/1011), [#1012](https://github.com/Psychotoxical/psysonic/pull/1012), [#1013](https://github.com/Psychotoxical/psysonic/pull/1013), [#1014](https://github.com/Psychotoxical/psysonic/pull/1014), [#1015](https://github.com/Psychotoxical/psysonic/pull/1015), [#1016](https://github.com/Psychotoxical/psysonic/pull/1016), [#1018](https://github.com/Psychotoxical/psysonic/pull/1018)**
|
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1009](https://github.com/Psychotoxical/psysonic/pull/1009), [#1011](https://github.com/Psychotoxical/psysonic/pull/1011), [#1012](https://github.com/Psychotoxical/psysonic/pull/1012), [#1013](https://github.com/Psychotoxical/psysonic/pull/1013), [#1014](https://github.com/Psychotoxical/psysonic/pull/1014), [#1015](https://github.com/Psychotoxical/psysonic/pull/1015), [#1016](https://github.com/Psychotoxical/psysonic/pull/1016), [#1018](https://github.com/Psychotoxical/psysonic/pull/1018), [#1020](https://github.com/Psychotoxical/psysonic/pull/1020)**
|
||||||
|
|
||||||
* New **Settings → Themes** tab: pick a theme, set the day/night scheduler, and browse a built-in **Theme Store** to install, update and uninstall community themes — with search, a dark/light filter, and full-size thumbnail previews.
|
* New **Settings → Themes** tab: pick a theme, set the day/night scheduler, and browse a built-in **Theme Store** to install, update and uninstall community themes — with search, a dark/light filter, and full-size thumbnail previews.
|
||||||
* The app now bundles six core themes (Catppuccin Mocha & Latte, Kanagawa Wave, Stark HUD, and the colour-blind-safe Vision Dark / Vision Navy); every other palette installs on demand from the [psysonic-themes](https://github.com/Psysonic/psysonic-themes) repo. Installed themes are saved locally and apply instantly at startup, even offline.
|
* The app now bundles six core themes (Catppuccin Mocha & Latte, Kanagawa Wave, Stark HUD, and the colour-blind-safe Vision Dark / Vision Navy); every other palette installs on demand from the [psysonic-themes](https://github.com/Psysonic/psysonic-themes) repo. Installed themes are saved locally and apply instantly at startup, even offline.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mod cover_cache;
|
|||||||
mod library_analysis_backfill;
|
mod library_analysis_backfill;
|
||||||
mod lib_commands;
|
mod lib_commands;
|
||||||
mod theme_import;
|
mod theme_import;
|
||||||
|
pub mod theme_animation;
|
||||||
|
|
||||||
pub use psysonic_integration::discord;
|
pub use psysonic_integration::discord;
|
||||||
|
|
||||||
@@ -653,6 +654,7 @@ pub fn run() {
|
|||||||
performance_cpu_snapshot,
|
performance_cpu_snapshot,
|
||||||
set_subsonic_wire_user_agent,
|
set_subsonic_wire_user_agent,
|
||||||
no_compositing_mode,
|
no_compositing_mode,
|
||||||
|
theme_animation_risk,
|
||||||
linux_xdg_session_type,
|
linux_xdg_session_type,
|
||||||
is_tiling_wm_cmd,
|
is_tiling_wm_cmd,
|
||||||
open_mini_player,
|
open_mini_player,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub(crate) use perf::performance_cpu_snapshot;
|
|||||||
pub(crate) use platform::{
|
pub(crate) use platform::{
|
||||||
linux_wayland_gpu_font_tuning_active, linux_wayland_text_render_settings_available,
|
linux_wayland_gpu_font_tuning_active, linux_wayland_text_render_settings_available,
|
||||||
set_linux_wayland_text_render_profile, set_linux_webkit_smooth_scrolling, set_window_decorations,
|
set_linux_wayland_text_render_profile, set_linux_webkit_smooth_scrolling, set_window_decorations,
|
||||||
|
theme_animation_risk,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub(crate) use platform::{
|
pub(crate) use platform::{
|
||||||
|
|||||||
@@ -137,6 +137,29 @@ pub(crate) fn linux_webkit_apply_wayland_gpu_font_tuning(win: &tauri::WebviewWin
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle native window decorations at runtime (Linux custom title bar opt-out).
|
/// Toggle native window decorations at runtime (Linux custom title bar opt-out).
|
||||||
|
/// Tauri command: true when theme animations may be costly on this setup —
|
||||||
|
/// Linux with the Nvidia WebKit quirk active (recorded once at startup) or
|
||||||
|
/// compositing forced off. The frontend warns on animated themes when true.
|
||||||
|
/// Always false off Linux.
|
||||||
|
#[tauri::command]
|
||||||
|
pub(crate) fn theme_animation_risk() -> bool {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
// Compositing forced off → GPU-accelerated effects/animation are costly.
|
||||||
|
if std::env::var("WEBKIT_DISABLE_COMPOSITING_MODE")
|
||||||
|
.map(|v| v == "1")
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
crate::theme_animation::nvidia_quirk_active()
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub(crate) fn set_window_decorations(enabled: bool, app_handle: tauri::AppHandle) {
|
pub(crate) fn set_window_decorations(enabled: bool, app_handle: tauri::AppHandle) {
|
||||||
if let Some(win) = app_handle.get_webview_window("main") {
|
if let Some(win) = app_handle.get_webview_window("main") {
|
||||||
|
|||||||
@@ -17,11 +17,16 @@ fn apply_linux_webkit_nvidia_quirk() {
|
|||||||
// may still be `XDG_SESSION_TYPE=wayland`. The quirk maps that to `__NV_DISABLE_EXPLICIT_SYNC`,
|
// may still be `XDG_SESSION_TYPE=wayland`. The quirk maps that to `__NV_DISABLE_EXPLICIT_SYNC`,
|
||||||
// which mismatches a real X11 EGL stack and can leave the webview gray — mirror the native-X11
|
// which mismatches a real X11 EGL stack and can leave the webview gray — mirror the native-X11
|
||||||
// branch (`WEBKIT_DISABLE_DMABUF_RENDERER` only) whenever GDK is pinned to x11 first in the list.
|
// branch (`WEBKIT_DISABLE_DMABUF_RENDERER` only) whenever GDK is pinned to x11 first in the list.
|
||||||
|
// Detect once and record it for the UI's animated-theme CPU-load warning,
|
||||||
|
// so the theme_animation_risk command never has to re-probe the GPU.
|
||||||
|
let kind = needs_workaround();
|
||||||
|
psysonic_lib::theme_animation::set_nvidia_quirk_active(!matches!(kind, WorkaroundKind::None));
|
||||||
|
|
||||||
let forced_x11_gdk = std::env::var("GDK_BACKEND").ok().is_some_and(|s| {
|
let forced_x11_gdk = std::env::var("GDK_BACKEND").ok().is_some_and(|s| {
|
||||||
matches!(s.split(',').next().map(str::trim), Some("x11"))
|
matches!(s.split(',').next().map(str::trim), Some("x11"))
|
||||||
});
|
});
|
||||||
if forced_x11_gdk {
|
if forced_x11_gdk {
|
||||||
match needs_workaround() {
|
match kind {
|
||||||
WorkaroundKind::None => {}
|
WorkaroundKind::None => {}
|
||||||
WorkaroundKind::DisableWebkitDmabufRenderer | WorkaroundKind::DisableNvExplicitSync => {
|
WorkaroundKind::DisableWebkitDmabufRenderer | WorkaroundKind::DisableNvExplicitSync => {
|
||||||
set_webkit_disable_dmabuf_renderer();
|
set_webkit_disable_dmabuf_renderer();
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//! Startup-recorded display hint for the theme system.
|
||||||
|
//!
|
||||||
|
//! The Nvidia/WebKit GPU quirk is detected once at process start (in `main()`,
|
||||||
|
//! before the webview/GTK init). We record whether it was needed so the UI can
|
||||||
|
//! warn that animated themes may raise CPU load on this setup — without
|
||||||
|
//! re-probing the GPU later. Read via the `theme_animation_risk` command
|
||||||
|
//! (`lib_commands::app_api::platform`).
|
||||||
|
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static NVIDIA_QUIRK_ACTIVE: OnceLock<bool> = OnceLock::new();
|
||||||
|
|
||||||
|
/// Record the startup Nvidia-WebKit-quirk detection. Called once from `main()`.
|
||||||
|
pub fn set_nvidia_quirk_active(active: bool) {
|
||||||
|
let _ = NVIDIA_QUIRK_ACTIVE.set(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the Nvidia WebKit quirk was needed at startup. False when unrecorded
|
||||||
|
/// (non-Linux, or GPU acceleration opted in via `PSYSONIC_WEBKIT_GPU_ACCEL`).
|
||||||
|
pub(crate) fn nvidia_quirk_active() -> bool {
|
||||||
|
NVIDIA_QUIRK_ACTIVE.get().copied().unwrap_or(false)
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Check, X } from 'lucide-react';
|
import { AlertTriangle, Check, X } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useThemeStore } from '../../store/themeStore';
|
import { useThemeStore } from '../../store/themeStore';
|
||||||
import { useInstalledThemesStore } from '../../store/installedThemesStore';
|
import { useInstalledThemesStore } from '../../store/installedThemesStore';
|
||||||
import { uninstallTheme } from '../../utils/themes/uninstallTheme';
|
import { uninstallTheme } from '../../utils/themes/uninstallTheme';
|
||||||
|
import { useThemeAnimationRisk } from '../../hooks/useThemeAnimationRisk';
|
||||||
import { FIXED_THEMES } from './fixedThemes';
|
import { FIXED_THEMES } from './fixedThemes';
|
||||||
|
|
||||||
/** Pull a 3-band swatch (bg / card / accent) out of an installed theme's CSS. */
|
/** Pull a 3-band swatch (bg / card / accent) out of an installed theme's CSS. */
|
||||||
@@ -26,6 +27,7 @@ interface Card {
|
|||||||
accent: string;
|
accent: string;
|
||||||
fixed: boolean;
|
fixed: boolean;
|
||||||
accessibility: boolean;
|
accessibility: boolean;
|
||||||
|
animated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,12 +41,13 @@ export function InstalledThemes() {
|
|||||||
const active = useThemeStore(s => s.theme);
|
const active = useThemeStore(s => s.theme);
|
||||||
const setTheme = useThemeStore(s => s.setTheme);
|
const setTheme = useThemeStore(s => s.setTheme);
|
||||||
const installed = useInstalledThemesStore(s => s.themes);
|
const installed = useInstalledThemesStore(s => s.themes);
|
||||||
|
const animRisk = useThemeAnimationRisk();
|
||||||
|
|
||||||
const cards: Card[] = [
|
const cards: Card[] = [
|
||||||
...FIXED_THEMES.map(f => ({ id: f.id, label: f.label, bg: f.bg, card: f.card, accent: f.accent, fixed: true, accessibility: !!f.accessibility })),
|
...FIXED_THEMES.map(f => ({ id: f.id, label: f.label, bg: f.bg, card: f.card, accent: f.accent, fixed: true, accessibility: !!f.accessibility, animated: false })),
|
||||||
...installed.map(it => {
|
...installed.map(it => {
|
||||||
const s = swatch(it.css);
|
const s = swatch(it.css);
|
||||||
return { id: it.id, label: it.name, bg: s.bg, card: s.card, accent: s.accent, fixed: false, accessibility: (it.tags || []).includes('accessibility') };
|
return { id: it.id, label: it.name, bg: s.bg, card: s.card, accent: s.accent, fixed: false, accessibility: (it.tags || []).includes('accessibility'), animated: /@(?:-[a-z]+-)?keyframes\b/i.test(it.css) };
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -101,6 +104,17 @@ export function InstalledThemes() {
|
|||||||
CVD-safe
|
CVD-safe
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{animRisk && c.animated && (
|
||||||
|
<span
|
||||||
|
role="img"
|
||||||
|
aria-label={t('settings.themeAnimationWarning')}
|
||||||
|
data-tooltip={t('settings.themeAnimationWarning')}
|
||||||
|
data-tooltip-pos="top"
|
||||||
|
style={{ display: 'inline-flex', color: 'var(--warning)', marginTop: 2 }}
|
||||||
|
>
|
||||||
|
<AlertTriangle size={12} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
{!c.fixed && (
|
{!c.fixed && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Check, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, WifiOff } from 'lucide-react';
|
import { AlertTriangle, Check, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, WifiOff } from 'lucide-react';
|
||||||
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
||||||
import CoverLightbox from '../CoverLightbox';
|
import CoverLightbox from '../CoverLightbox';
|
||||||
|
import { useThemeAnimationRisk } from '../../hooks/useThemeAnimationRisk';
|
||||||
import { useThemeStore } from '../../store/themeStore';
|
import { useThemeStore } from '../../store/themeStore';
|
||||||
import { useInstalledThemesStore, type InstalledTheme } from '../../store/installedThemesStore';
|
import { useInstalledThemesStore, type InstalledTheme } from '../../store/installedThemesStore';
|
||||||
import {
|
import {
|
||||||
@@ -33,6 +34,7 @@ export function ThemeStoreSection() {
|
|||||||
const setTheme = useThemeStore(s => s.setTheme);
|
const setTheme = useThemeStore(s => s.setTheme);
|
||||||
const installed = useInstalledThemesStore(s => s.themes);
|
const installed = useInstalledThemesStore(s => s.themes);
|
||||||
const install = useInstalledThemesStore(s => s.install);
|
const install = useInstalledThemesStore(s => s.install);
|
||||||
|
const animRisk = useThemeAnimationRisk();
|
||||||
|
|
||||||
const [themes, setThemes] = useState<RegistryTheme[] | null>(null);
|
const [themes, setThemes] = useState<RegistryTheme[] | null>(null);
|
||||||
const [generatedAt, setGeneratedAt] = useState('');
|
const [generatedAt, setGeneratedAt] = useState('');
|
||||||
@@ -293,6 +295,17 @@ export function ThemeStoreSection() {
|
|||||||
<Check size={12} /> {t('settings.themeStoreActive')}
|
<Check size={12} /> {t('settings.themeStoreActive')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{animRisk && th.animated && (
|
||||||
|
<span
|
||||||
|
role="img"
|
||||||
|
aria-label={t('settings.themeAnimationWarning')}
|
||||||
|
data-tooltip={t('settings.themeAnimationWarning')}
|
||||||
|
data-tooltip-pos="top"
|
||||||
|
style={{ display: 'inline-flex', color: 'var(--warning)' }}
|
||||||
|
>
|
||||||
|
<AlertTriangle size={14} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
|
<div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
|
||||||
{t('settings.themeStoreByAuthor', { author: th.author })}
|
{t('settings.themeStoreByAuthor', { author: th.author })}
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ const CONTRIBUTOR_ENTRIES = [
|
|||||||
'Performance Probe: Monitor/Toggles redesign, live CPU/RSS/thread metrics, overlay pins and sparklines, macOS snapshots (PR #890)',
|
'Performance Probe: Monitor/Toggles redesign, live CPU/RSS/thread metrics, overlay pins and sparklines, macOS snapshots (PR #890)',
|
||||||
'Performance Probe: opt-in thread-group CPU poll toggle and includeThreadGroups IPC fix (PR #891)',
|
'Performance Probe: opt-in thread-group CPU poll toggle and includeThreadGroups IPC fix (PR #891)',
|
||||||
'Queue: switchable display mode — Queue (upcoming only) vs Playlist (full list), with header toggle and settings entry (PR #922)',
|
'Queue: switchable display mode — Queue (upcoming only) vs Playlist (full list), with header toggle and settings entry (PR #922)',
|
||||||
'Community Theme Store: semantic-token refactor, dedicated Themes tab with day/night scheduler, install/update/uninstall, local .zip import, and free-form community themes (safety floor + state-reactive styling); 80+ palettes moved to an on-demand CDN repo (PR #1009, #1011, #1012, #1013, #1014, #1015, #1016, #1018)',
|
'Community Theme Store: semantic-token refactor, dedicated Themes tab with day/night scheduler, install/update/uninstall, local .zip import, and free-form community themes (safety floor + state-reactive styling); 80+ palettes moved to an on-demand CDN repo (PR #1009, #1011, #1012, #1013, #1014, #1015, #1016, #1018, #1020)',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { IS_LINUX } from '../utils/platform';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether animated themes may raise CPU load on this setup — Linux with the
|
||||||
|
* Nvidia WebKit quirk active (recorded at startup) or compositing forced off.
|
||||||
|
* The store / theme cards show a warning on animated themes when this is true.
|
||||||
|
*
|
||||||
|
* Fetched once and cached for the process (read-once, per the Tauri-boundary
|
||||||
|
* rule). Always false off Linux, so the `invoke` is skipped there.
|
||||||
|
*/
|
||||||
|
let cached: boolean | null = null;
|
||||||
|
|
||||||
|
export function useThemeAnimationRisk(): boolean {
|
||||||
|
const [risk, setRisk] = useState(cached ?? false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (cached !== null) {
|
||||||
|
setRisk(cached);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IS_LINUX) {
|
||||||
|
cached = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let alive = true;
|
||||||
|
const p = invoke<boolean>('theme_animation_risk');
|
||||||
|
// Guard the mocked-in-tests case where invoke isn't a real promise.
|
||||||
|
if (p && typeof (p as Promise<boolean>).then === 'function') {
|
||||||
|
(p as Promise<boolean>)
|
||||||
|
.then((v) => {
|
||||||
|
cached = !!v;
|
||||||
|
if (alive) setRisk(cached);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
cached = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
alive = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return risk;
|
||||||
|
}
|
||||||
@@ -324,6 +324,7 @@ export const settings = {
|
|||||||
tabThemes: 'Themes',
|
tabThemes: 'Themes',
|
||||||
themesYourThemesTitle: 'Deine Themes',
|
themesYourThemesTitle: 'Deine Themes',
|
||||||
themesCvdTooltip: 'Farbfehlsichtigkeits-sicher – Deuteranopie, Protanopie, Tritanopie',
|
themesCvdTooltip: 'Farbfehlsichtigkeits-sicher – Deuteranopie, Protanopie, Tritanopie',
|
||||||
|
themeAnimationWarning: 'Dieses Theme nutzt Animationen, die auf deinem Setup (Nvidia/Linux oder Compositing aus) die CPU-Last erhöhen können.',
|
||||||
themeStoreTitle: 'Theme-Store',
|
themeStoreTitle: 'Theme-Store',
|
||||||
themeStoreSubmitText: 'Eigenes Theme gebaut? Teil es mit der Community — mehr Infos im Themes-Repository.',
|
themeStoreSubmitText: 'Eigenes Theme gebaut? Teil es mit der Community — mehr Infos im Themes-Repository.',
|
||||||
themeStoreSubmitLink: 'Themes-Repository öffnen',
|
themeStoreSubmitLink: 'Themes-Repository öffnen',
|
||||||
|
|||||||
@@ -391,6 +391,7 @@ export const settings = {
|
|||||||
tabThemes: 'Themes',
|
tabThemes: 'Themes',
|
||||||
themesYourThemesTitle: 'Your Themes',
|
themesYourThemesTitle: 'Your Themes',
|
||||||
themesCvdTooltip: 'Colour-blind safe — deuteranopia, protanopia, tritanopia',
|
themesCvdTooltip: 'Colour-blind safe — deuteranopia, protanopia, tritanopia',
|
||||||
|
themeAnimationWarning: 'This theme uses animations that may raise CPU usage on your setup (Nvidia/Linux or compositing off).',
|
||||||
themeStoreTitle: 'Theme Store',
|
themeStoreTitle: 'Theme Store',
|
||||||
themeStoreSubmitText: 'Made your own theme? Share it with the community — more info in the themes repository.',
|
themeStoreSubmitText: 'Made your own theme? Share it with the community — more info in the themes repository.',
|
||||||
themeStoreSubmitLink: 'Open the themes repository',
|
themeStoreSubmitLink: 'Open the themes repository',
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ export const settings = {
|
|||||||
tabThemes: 'Temas',
|
tabThemes: 'Temas',
|
||||||
themesYourThemesTitle: 'Tus temas',
|
themesYourThemesTitle: 'Tus temas',
|
||||||
themesCvdTooltip: 'Apto para daltonismo: deuteranopía, protanopía, tritanopía',
|
themesCvdTooltip: 'Apto para daltonismo: deuteranopía, protanopía, tritanopía',
|
||||||
|
themeAnimationWarning: 'Este tema usa animaciones que pueden aumentar el uso de CPU en tu sistema (Nvidia/Linux o compositing desactivado).',
|
||||||
themeStoreTitle: 'Tienda de temas',
|
themeStoreTitle: 'Tienda de temas',
|
||||||
themeStoreSubmitText: '¿Has creado tu propio tema? Compártelo con la comunidad — más información en el repositorio de temas.',
|
themeStoreSubmitText: '¿Has creado tu propio tema? Compártelo con la comunidad — más información en el repositorio de temas.',
|
||||||
themeStoreSubmitLink: 'Abrir el repositorio de temas',
|
themeStoreSubmitLink: 'Abrir el repositorio de temas',
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ export const settings = {
|
|||||||
tabThemes: 'Thèmes',
|
tabThemes: 'Thèmes',
|
||||||
themesYourThemesTitle: 'Vos thèmes',
|
themesYourThemesTitle: 'Vos thèmes',
|
||||||
themesCvdTooltip: 'Adapté au daltonisme — deutéranopie, protanopie, tritanopie',
|
themesCvdTooltip: 'Adapté au daltonisme — deutéranopie, protanopie, tritanopie',
|
||||||
|
themeAnimationWarning: 'Ce thème utilise des animations qui peuvent augmenter l’utilisation du processeur sur votre configuration (Nvidia/Linux ou compositing désactivé).',
|
||||||
themeStoreTitle: 'Boutique de thèmes',
|
themeStoreTitle: 'Boutique de thèmes',
|
||||||
themeStoreSubmitText: "Vous avez créé votre propre thème ? Partagez-le avec la communauté — plus d'infos dans le dépôt de thèmes.",
|
themeStoreSubmitText: "Vous avez créé votre propre thème ? Partagez-le avec la communauté — plus d'infos dans le dépôt de thèmes.",
|
||||||
themeStoreSubmitLink: 'Ouvrir le dépôt de thèmes',
|
themeStoreSubmitLink: 'Ouvrir le dépôt de thèmes',
|
||||||
|
|||||||
@@ -323,6 +323,7 @@ export const settings = {
|
|||||||
tabThemes: 'Temaer',
|
tabThemes: 'Temaer',
|
||||||
themesYourThemesTitle: 'Dine temaer',
|
themesYourThemesTitle: 'Dine temaer',
|
||||||
themesCvdTooltip: 'Fargeblind-sikker – deuteranopi, protanopi, tritanopi',
|
themesCvdTooltip: 'Fargeblind-sikker – deuteranopi, protanopi, tritanopi',
|
||||||
|
themeAnimationWarning: 'Dette temaet bruker animasjoner som kan øke CPU-bruken på oppsettet ditt (Nvidia/Linux eller compositing av).',
|
||||||
themeStoreTitle: 'Temabutikk',
|
themeStoreTitle: 'Temabutikk',
|
||||||
themeStoreSubmitText: 'Laget ditt eget tema? Del det med fellesskapet — mer info i tema-repoet.',
|
themeStoreSubmitText: 'Laget ditt eget tema? Del det med fellesskapet — mer info i tema-repoet.',
|
||||||
themeStoreSubmitLink: 'Åpne tema-repoet',
|
themeStoreSubmitLink: 'Åpne tema-repoet',
|
||||||
|
|||||||
@@ -320,6 +320,7 @@ export const settings = {
|
|||||||
tabThemes: "Thema's",
|
tabThemes: "Thema's",
|
||||||
themesYourThemesTitle: "Jouw thema's",
|
themesYourThemesTitle: "Jouw thema's",
|
||||||
themesCvdTooltip: 'Kleurenblind-veilig – deuteranopie, protanopie, tritanopie',
|
themesCvdTooltip: 'Kleurenblind-veilig – deuteranopie, protanopie, tritanopie',
|
||||||
|
themeAnimationWarning: 'Dit thema gebruikt animaties die het CPU-gebruik op jouw systeem kunnen verhogen (Nvidia/Linux of compositing uit).',
|
||||||
themeStoreTitle: 'Themawinkel',
|
themeStoreTitle: 'Themawinkel',
|
||||||
themeStoreSubmitText: 'Eigen thema gemaakt? Deel het met de community — meer info in de themarepository.',
|
themeStoreSubmitText: 'Eigen thema gemaakt? Deel het met de community — meer info in de themarepository.',
|
||||||
themeStoreSubmitLink: 'Themarepository openen',
|
themeStoreSubmitLink: 'Themarepository openen',
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ export const settings = {
|
|||||||
tabThemes: 'Teme',
|
tabThemes: 'Teme',
|
||||||
themesYourThemesTitle: 'Temele tale',
|
themesYourThemesTitle: 'Temele tale',
|
||||||
themesCvdTooltip: 'Sigur pentru daltonism – deuteranopie, protanopie, tritanopie',
|
themesCvdTooltip: 'Sigur pentru daltonism – deuteranopie, protanopie, tritanopie',
|
||||||
|
themeAnimationWarning: 'Această temă folosește animații care pot crește utilizarea procesorului pe configurația ta (Nvidia/Linux sau compositing dezactivat).',
|
||||||
themeStoreTitle: 'Magazin de teme',
|
themeStoreTitle: 'Magazin de teme',
|
||||||
themeStoreSubmitText: 'Ți-ai creat propria temă? Împărtășește-o cu comunitatea — mai multe informații în depozitul de teme.',
|
themeStoreSubmitText: 'Ți-ai creat propria temă? Împărtășește-o cu comunitatea — mai multe informații în depozitul de teme.',
|
||||||
themeStoreSubmitLink: 'Deschide depozitul de teme',
|
themeStoreSubmitLink: 'Deschide depozitul de teme',
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ export const settings = {
|
|||||||
tabThemes: 'Темы',
|
tabThemes: 'Темы',
|
||||||
themesYourThemesTitle: 'Ваши темы',
|
themesYourThemesTitle: 'Ваши темы',
|
||||||
themesCvdTooltip: 'Безопасно при дальтонизме — дейтеранопия, протанопия, тританопия',
|
themesCvdTooltip: 'Безопасно при дальтонизме — дейтеранопия, протанопия, тританопия',
|
||||||
|
themeAnimationWarning: 'Эта тема использует анимации, которые могут повысить нагрузку на процессор в вашей конфигурации (Nvidia/Linux или композитинг выключен).',
|
||||||
themeStoreTitle: 'Магазин тем',
|
themeStoreTitle: 'Магазин тем',
|
||||||
themeStoreSubmitText: 'Создали свою тему? Поделитесь с сообществом — подробности в репозитории тем.',
|
themeStoreSubmitText: 'Создали свою тему? Поделитесь с сообществом — подробности в репозитории тем.',
|
||||||
themeStoreSubmitLink: 'Открыть репозиторий тем',
|
themeStoreSubmitLink: 'Открыть репозиторий тем',
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ export const settings = {
|
|||||||
tabThemes: '主题',
|
tabThemes: '主题',
|
||||||
themesYourThemesTitle: '你的主题',
|
themesYourThemesTitle: '你的主题',
|
||||||
themesCvdTooltip: '色觉障碍友好 — 绿色盲、红色盲、蓝色盲',
|
themesCvdTooltip: '色觉障碍友好 — 绿色盲、红色盲、蓝色盲',
|
||||||
|
themeAnimationWarning: '此主题使用动画,在你的环境(Nvidia/Linux 或关闭合成)下可能会增加 CPU 占用。',
|
||||||
themeStoreTitle: '主题商店',
|
themeStoreTitle: '主题商店',
|
||||||
themeStoreSubmitText: '做了自己的主题?与社区分享——更多信息见主题仓库。',
|
themeStoreSubmitText: '做了自己的主题?与社区分享——更多信息见主题仓库。',
|
||||||
themeStoreSubmitLink: '打开主题仓库',
|
themeStoreSubmitLink: '打开主题仓库',
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export interface RegistryTheme {
|
|||||||
description: string;
|
description: string;
|
||||||
mode: 'dark' | 'light';
|
mode: 'dark' | 'light';
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
|
/** True when the theme defines @keyframes (used for the CPU-load warning). */
|
||||||
|
animated?: boolean;
|
||||||
/** Repo-relative path to the theme's CSS. */
|
/** Repo-relative path to the theme's CSS. */
|
||||||
css: string;
|
css: string;
|
||||||
/** Repo-relative path to the thumbnail. */
|
/** Repo-relative path to the thumbnail. */
|
||||||
|
|||||||
Reference in New Issue
Block a user