mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
Backing out #281 for now — follow-up coming with the chrome restyle path bounded more tightly. Will re-land with the corrected sweep ordering.
This commit is contained in:
committed by
GitHub
parent
c54aa22e6b
commit
e721b3060d
+2
-2
@@ -1088,7 +1088,7 @@ export default function App() {
|
|||||||
useGlobalShortcutsStore.getState().registerAll();
|
useGlobalShortcutsStore.getState().registerAll();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// ── Lol: Ctrl+Shift+Alt+N → export new albums image ──
|
// ── Easter egg: Ctrl+Shift+Alt+N → export new albums image ──
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const onKey = (e: KeyboardEvent) => {
|
||||||
if (!e.ctrlKey || !e.shiftKey || !e.altKey || e.code !== 'KeyN') return;
|
if (!e.ctrlKey || !e.shiftKey || !e.altKey || e.code !== 'KeyN') return;
|
||||||
@@ -1112,7 +1112,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast(`❌ Export fehlgeschlagen: ${String(err).slice(0, 80)}`);
|
showToast(`❌ Export fehlgeschlagen: ${String(err).slice(0, 80)}`);
|
||||||
console.error('[lol] export failed:', err);
|
console.error('[easter egg] export failed:', err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
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
+11
-2
@@ -21,7 +21,6 @@ import { lastfmGetToken, lastfmAuthUrl, lastfmGetSession, lastfmGetUserInfo, Las
|
|||||||
import LastfmIcon from '../components/LastfmIcon';
|
import LastfmIcon from '../components/LastfmIcon';
|
||||||
import CustomSelect from '../components/CustomSelect';
|
import CustomSelect from '../components/CustomSelect';
|
||||||
import SettingsSubSection from '../components/SettingsSubSection';
|
import SettingsSubSection from '../components/SettingsSubSection';
|
||||||
import { AboutPsysonicBrandHeader } from '../components/AboutPsysonicLol';
|
|
||||||
import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable';
|
import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable';
|
||||||
import ThemePicker, { THEME_GROUPS } from '../components/ThemePicker';
|
import ThemePicker, { THEME_GROUPS } from '../components/ThemePicker';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
@@ -3985,7 +3984,17 @@ export default function Settings() {
|
|||||||
icon={<Info size={16} />}
|
icon={<Info size={16} />}
|
||||||
>
|
>
|
||||||
<div className="settings-card settings-about">
|
<div className="settings-card settings-about">
|
||||||
<AboutPsysonicBrandHeader appVersion={appVersion} aboutVersionLabel={t('settings.aboutVersion')} />
|
<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>
|
||||||
|
|
||||||
<p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, margin: '1rem 0 0.5rem' }}>
|
<p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, margin: '1rem 0 0.5rem' }}>
|
||||||
{t('settings.aboutDesc')}
|
{t('settings.aboutDesc')}
|
||||||
|
|||||||
@@ -3756,168 +3756,6 @@
|
|||||||
gap: var(--space-4);
|
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 */
|
||||||
.toggle-switch {
|
.toggle-switch {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
Reference in New Issue
Block a user