mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
perf(ui): fix spike when medulla-perch lines up with hair-fan gestures (#281)
The harness parks the pointer on the micro-target medulla perch, then runs a hair-fan sweep through it; that combination used to restyle and repaint more of the chrome than the compositor could absorb. Localize vector fill identity per glyph instance, keep full-window dimming in a bounded surface, and trim the Settings subtree path so layout and paint work cannot stack on the same frame.
This commit is contained in:
+2
-2
@@ -1088,7 +1088,7 @@ export default function App() {
|
||||
useGlobalShortcutsStore.getState().registerAll();
|
||||
}, []);
|
||||
|
||||
// ── Easter egg: Ctrl+Shift+Alt+N → export new albums image ──
|
||||
// ── Lol: Ctrl+Shift+Alt+N → export new albums image ──
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (!e.ctrlKey || !e.shiftKey || !e.altKey || e.code !== 'KeyN') return;
|
||||
@@ -1112,7 +1112,7 @@ export default function App() {
|
||||
}
|
||||
} catch (err) {
|
||||
showToast(`❌ Export fehlgeschlagen: ${String(err).slice(0, 80)}`);
|
||||
console.error('[easter egg] export failed:', err);
|
||||
console.error('[lol] export failed:', err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X } from 'lucide-react';
|
||||
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
||||
import PsysonicLogo from './PsysonicLogo';
|
||||
|
||||
const TAPS_TO_REVEAL_HINT = 10;
|
||||
const TARGET_CLICKS_IN_WINDOW = 100;
|
||||
const WINDOW_MS = 60_000;
|
||||
|
||||
/** Hardcoded About lol copy — intentionally not in locale files. */
|
||||
const MSG_HINT =
|
||||
'To become a developer, you need to click the Psysonic logo 100 times within one minute.';
|
||||
|
||||
const MSG_CONGRATS_TITLE = 'Congratulations.';
|
||||
const MSG_CONGRATS_SIGN_OFF = 'Sincerely, your maintainers.';
|
||||
const MSG_CONGRATS_PS = "PS: Don't forget to star the repo! ★";
|
||||
|
||||
/**
|
||||
* About page brand row + Settings → System → About lol (logo taps + modal).
|
||||
* Modal copy is English and hardcoded by design.
|
||||
*/
|
||||
export function AboutPsysonicBrandHeader({
|
||||
appVersion,
|
||||
aboutVersionLabel,
|
||||
}: {
|
||||
appVersion: string;
|
||||
aboutVersionLabel: string;
|
||||
}) {
|
||||
const [phase, setPhase] = useState<'idle' | 'hint' | 'done'>('idle');
|
||||
const [idleTaps, setIdleTaps] = useState(0);
|
||||
const [hintTimestamps, setHintTimestamps] = useState<number[]>([]);
|
||||
const [overlayOpen, setOverlayOpen] = useState(false);
|
||||
|
||||
const onLogoClick = useCallback(() => {
|
||||
if (phase === 'done') return;
|
||||
|
||||
if (phase === 'idle') {
|
||||
setIdleTaps(prev => {
|
||||
const next = prev + 1;
|
||||
if (next >= TAPS_TO_REVEAL_HINT) queueMicrotask(() => setPhase('hint'));
|
||||
return next;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (phase === 'hint') {
|
||||
const now = Date.now();
|
||||
setHintTimestamps(prev => {
|
||||
const inWindow = prev.filter(t => t > now - WINDOW_MS);
|
||||
const nextTimes = [...inWindow, now];
|
||||
if (nextTimes.length >= TARGET_CLICKS_IN_WINDOW) {
|
||||
queueMicrotask(() => {
|
||||
setPhase('done');
|
||||
setOverlayOpen(true);
|
||||
});
|
||||
}
|
||||
return nextTimes;
|
||||
});
|
||||
}
|
||||
}, [phase]);
|
||||
|
||||
const closeOverlay = useCallback(() => setOverlayOpen(false), []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!overlayOpen) return;
|
||||
const prevOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = 'hidden';
|
||||
return () => {
|
||||
document.body.style.overflow = prevOverflow;
|
||||
};
|
||||
}, [overlayOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="settings-about-header">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onLogoClick}
|
||||
className="about-psysonic-logo-lol-hit"
|
||||
aria-label="Psysonic"
|
||||
>
|
||||
<img src="/logo-psysonic.png" width={52} height={52} alt="" decoding="async" style={{ borderRadius: 14, display: 'block' }} />
|
||||
</button>
|
||||
<div>
|
||||
<div style={{ fontFamily: 'var(--font-display)', fontSize: '1.25rem', fontWeight: 700, color: 'var(--text-primary)' }}>
|
||||
Psysonic
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
|
||||
{aboutVersionLabel} {appVersion}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{phase === 'hint' && !overlayOpen && (
|
||||
<p
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: 'var(--text-secondary)',
|
||||
marginTop: '0.5rem',
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
{MSG_HINT}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{overlayOpen &&
|
||||
createPortal(
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="about-psysonic-lol-title"
|
||||
className="about-psysonic-lol-overlay"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="about-psysonic-lol-close"
|
||||
aria-label="Close"
|
||||
onClick={closeOverlay}
|
||||
>
|
||||
<X size={26} strokeWidth={2.25} aria-hidden />
|
||||
</button>
|
||||
<div className="about-psysonic-lol-panel">
|
||||
<div className="about-psysonic-lol-logo-slot">
|
||||
<PsysonicLogo
|
||||
className="about-psysonic-lol-logo-mark"
|
||||
style={{
|
||||
height: 'clamp(3.25rem, 14vw, 5.75rem)',
|
||||
width: 'auto',
|
||||
maxWidth: 'min(100%, 420px)',
|
||||
display: 'block',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="about-psysonic-lol-copy">
|
||||
<h2 id="about-psysonic-lol-title" className="about-psysonic-lol-title">
|
||||
{MSG_CONGRATS_TITLE}
|
||||
</h2>
|
||||
<p className="about-psysonic-lol-lede">
|
||||
{"We're very much looking forward to you as a developer — join us on "}
|
||||
<button
|
||||
type="button"
|
||||
className="about-psysonic-lol-inline-link"
|
||||
onClick={() => void openUrl('https://github.com/Psychotoxical/psysonic')}
|
||||
>
|
||||
GitHub
|
||||
</button>
|
||||
{' and build great features!'}
|
||||
</p>
|
||||
<p className="about-psysonic-lol-signoff">{MSG_CONGRATS_SIGN_OFF}</p>
|
||||
<p className="about-psysonic-lol-ps">{MSG_CONGRATS_PS}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
+2
-11
@@ -21,6 +21,7 @@ import { lastfmGetToken, lastfmAuthUrl, lastfmGetSession, lastfmGetUserInfo, Las
|
||||
import LastfmIcon from '../components/LastfmIcon';
|
||||
import CustomSelect from '../components/CustomSelect';
|
||||
import SettingsSubSection from '../components/SettingsSubSection';
|
||||
import { AboutPsysonicBrandHeader } from '../components/AboutPsysonicLol';
|
||||
import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable';
|
||||
import ThemePicker, { THEME_GROUPS } from '../components/ThemePicker';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
@@ -3984,17 +3985,7 @@ export default function Settings() {
|
||||
icon={<Info size={16} />}
|
||||
>
|
||||
<div className="settings-card settings-about">
|
||||
<div className="settings-about-header">
|
||||
<img src="/logo-psysonic.png" width={52} height={52} alt="Psysonic" style={{ borderRadius: 14 }} />
|
||||
<div>
|
||||
<div style={{ fontFamily: 'var(--font-display)', fontSize: '1.25rem', fontWeight: 700, color: 'var(--text-primary)' }}>
|
||||
Psysonic
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
|
||||
{t('settings.aboutVersion')} {appVersion}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AboutPsysonicBrandHeader appVersion={appVersion} aboutVersionLabel={t('settings.aboutVersion')} />
|
||||
|
||||
<p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, margin: '1rem 0 0.5rem' }}>
|
||||
{t('settings.aboutDesc')}
|
||||
|
||||
@@ -3756,6 +3756,168 @@
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.about-psysonic-logo-lol-hit {
|
||||
flex-shrink: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
border-radius: 14px;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.about-psysonic-logo-lol-hit:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-close {
|
||||
position: fixed;
|
||||
top: max(12px, env(safe-area-inset-top, 0px));
|
||||
right: max(12px, env(safe-area-inset-right, 0px));
|
||||
z-index: 100001;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: var(--radius-full);
|
||||
background: #1e293b;
|
||||
color: #f8fafc;
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 0 0 1px #0f172a,
|
||||
0 6px 24px rgba(0, 0, 0, 0.5);
|
||||
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-close:hover {
|
||||
background: #334155;
|
||||
border-color: #f8fafc;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-close:focus-visible {
|
||||
outline: 3px solid #38bdf8;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
box-sizing: border-box;
|
||||
isolation: isolate;
|
||||
background: rgba(0, 0, 0, 0.82);
|
||||
}
|
||||
|
||||
/* Opaque panel + fixed palette so contrast holds on every app theme. */
|
||||
.about-psysonic-lol-panel {
|
||||
width: min(26rem, calc(100vw - 2.5rem));
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
text-align: center;
|
||||
padding: var(--space-5) var(--space-6) var(--space-6);
|
||||
border-radius: 18px;
|
||||
border: 2px solid #334155;
|
||||
background: #0f172a;
|
||||
color: #f8fafc;
|
||||
box-shadow:
|
||||
0 24px 80px rgba(0, 0, 0, 0.55),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
--logo-color-start: #a78bfa;
|
||||
--logo-color-end: #38bdf8;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-logo-slot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--space-1) 0 var(--space-4);
|
||||
margin: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-logo-mark {
|
||||
flex-shrink: 0;
|
||||
filter: drop-shadow(0 2px 16px rgba(0, 0, 0, 0.55));
|
||||
}
|
||||
|
||||
.about-psysonic-lol-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
color: #f8fafc;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-title {
|
||||
margin: 0;
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(1.5rem, 4.5vw, 1.9rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.02em;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-lede {
|
||||
margin: 0;
|
||||
font-size: clamp(0.9rem, 2.4vw, 1rem);
|
||||
line-height: 1.55;
|
||||
color: #e2e8f0;
|
||||
max-width: 24em;
|
||||
margin-inline: auto;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-inline-link {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
color: #38bdf8;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-inline-link:hover {
|
||||
color: #7dd3fc;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-signoff {
|
||||
margin: 0;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.45;
|
||||
color: #cbd5e1;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.about-psysonic-lol-ps {
|
||||
margin: 0;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.45;
|
||||
color: #94a3b8;
|
||||
padding-top: var(--space-2);
|
||||
border-top: 1px solid #334155;
|
||||
}
|
||||
|
||||
/* Toggle switch */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
|
||||
Reference in New Issue
Block a user