mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
refactor(utils): group utils/ files into topic folders (Phase L, part 1) (#689)
111 of 122 top-level src/utils/ files move into 16 topic folders (audio, cache, cover, share, server, playback, playlist, deviceSync, waveform, mix, format, export, changelog, ui, perf, componentHelpers). True singletons with no cluster stay at the utils/ root. Pure file-move: a path-aware codemod rewrote 539 relative-import specifiers across 275 files; no logic touched. The hot-path coverage gate list (.github/frontend-hot-path-files.txt) is updated to the new paths for the 11 gated utils files — a mechanical consequence of the move, not a CI change. tsc is green.
This commit is contained in:
committed by
GitHub
parent
2409a1fec8
commit
7a7a9f5e6b
@@ -0,0 +1,161 @@
|
||||
import { useSyncExternalStore } from 'react';
|
||||
|
||||
export type PerfProbeFlags = {
|
||||
/** On-screen rAF-based FPS counter (Performance Probe). */
|
||||
showFpsOverlay: boolean;
|
||||
disableWaveformCanvas: boolean;
|
||||
disablePlayerProgressUi: boolean;
|
||||
disableMarqueeScroll: boolean;
|
||||
disableBackdropBlur: boolean;
|
||||
disableCssAnimations: boolean;
|
||||
disableOverlayScrollbars: boolean;
|
||||
disableTooltipPortal: boolean;
|
||||
disableQueuePanelMount: boolean;
|
||||
disableBackgroundPolling: boolean;
|
||||
disableMainRouteContentMount: boolean;
|
||||
disableMainstageHero: boolean;
|
||||
disableMainstageRails: boolean;
|
||||
disableMainstageGridCards: boolean;
|
||||
disableMainstageVirtualLists: boolean;
|
||||
disableMainstageStickyHeader: boolean;
|
||||
disableMainstageHeroBackdrop: boolean;
|
||||
disableMainstageRailArtwork: boolean;
|
||||
disableMainstageRailInteractivity: boolean;
|
||||
disableHomeAlbumRows: boolean;
|
||||
disableHomeSongRails: boolean;
|
||||
disableHomeRailArtwork: boolean;
|
||||
disableHomeArtworkFx: boolean;
|
||||
disableHomeArtworkClip: boolean;
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'psysonic_perf_probe_flags_v1';
|
||||
|
||||
const DEFAULT_FLAGS: PerfProbeFlags = {
|
||||
showFpsOverlay: false,
|
||||
disableWaveformCanvas: false,
|
||||
disablePlayerProgressUi: false,
|
||||
disableMarqueeScroll: false,
|
||||
disableBackdropBlur: false,
|
||||
disableCssAnimations: false,
|
||||
disableOverlayScrollbars: false,
|
||||
disableTooltipPortal: false,
|
||||
disableQueuePanelMount: false,
|
||||
disableBackgroundPolling: false,
|
||||
disableMainRouteContentMount: false,
|
||||
disableMainstageHero: false,
|
||||
disableMainstageRails: false,
|
||||
disableMainstageGridCards: false,
|
||||
disableMainstageVirtualLists: false,
|
||||
disableMainstageStickyHeader: false,
|
||||
disableMainstageHeroBackdrop: false,
|
||||
disableMainstageRailArtwork: false,
|
||||
disableMainstageRailInteractivity: false,
|
||||
disableHomeAlbumRows: false,
|
||||
disableHomeSongRails: false,
|
||||
disableHomeRailArtwork: false,
|
||||
disableHomeArtworkFx: false,
|
||||
disableHomeArtworkClip: false,
|
||||
};
|
||||
|
||||
let flags: PerfProbeFlags = { ...DEFAULT_FLAGS };
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
function safeParseFlags(raw: string | null): Partial<PerfProbeFlags> {
|
||||
if (!raw) return {};
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as Partial<PerfProbeFlags>;
|
||||
return parsed ?? {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function applyFlagsToDom(next: PerfProbeFlags): void {
|
||||
if (typeof document === 'undefined') return;
|
||||
const root = document.documentElement;
|
||||
root.dataset.perfDisableWaveform = next.disableWaveformCanvas ? 'true' : 'false';
|
||||
root.dataset.perfDisablePlayerProgressUi = next.disablePlayerProgressUi ? 'true' : 'false';
|
||||
root.dataset.perfDisableMarquee = next.disableMarqueeScroll ? 'true' : 'false';
|
||||
root.dataset.perfDisableBlur = next.disableBackdropBlur ? 'true' : 'false';
|
||||
root.dataset.perfDisableAnimations = next.disableCssAnimations ? 'true' : 'false';
|
||||
root.dataset.perfDisableOverlayScroll = next.disableOverlayScrollbars ? 'true' : 'false';
|
||||
root.dataset.perfDisableTooltipPortal = next.disableTooltipPortal ? 'true' : 'false';
|
||||
root.dataset.perfDisableQueuePanel = next.disableQueuePanelMount ? 'true' : 'false';
|
||||
root.dataset.perfDisableBackgroundPolling = next.disableBackgroundPolling ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainRoute = next.disableMainRouteContentMount ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageHero = next.disableMainstageHero ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageRails = next.disableMainstageRails ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageGrid = next.disableMainstageGridCards ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageVirtual = next.disableMainstageVirtualLists ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageHeader = next.disableMainstageStickyHeader ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageHeroBackdrop = next.disableMainstageHeroBackdrop ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageRailArtwork = next.disableMainstageRailArtwork ? 'true' : 'false';
|
||||
root.dataset.perfDisableMainstageRailInteractivity = next.disableMainstageRailInteractivity ? 'true' : 'false';
|
||||
root.dataset.perfDisableHomeAlbumRows = next.disableHomeAlbumRows ? 'true' : 'false';
|
||||
root.dataset.perfDisableHomeSongRails = next.disableHomeSongRails ? 'true' : 'false';
|
||||
root.dataset.perfDisableHomeRailArtwork = next.disableHomeRailArtwork ? 'true' : 'false';
|
||||
root.dataset.perfDisableHomeArtworkFx = next.disableHomeArtworkFx ? 'true' : 'false';
|
||||
root.dataset.perfDisableHomeArtworkClip = next.disableHomeArtworkClip ? 'true' : 'false';
|
||||
}
|
||||
|
||||
function persistFlags(next: PerfProbeFlags): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
try {
|
||||
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||||
} catch {
|
||||
// Ignore storage errors; runtime state still works.
|
||||
}
|
||||
}
|
||||
|
||||
function emit(): void {
|
||||
listeners.forEach(fn => fn());
|
||||
}
|
||||
|
||||
function setFlags(next: PerfProbeFlags): void {
|
||||
flags = next;
|
||||
applyFlagsToDom(flags);
|
||||
persistFlags(flags);
|
||||
emit();
|
||||
}
|
||||
|
||||
function initFlags(): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
const fromStorage = safeParseFlags(window.localStorage.getItem(STORAGE_KEY));
|
||||
flags = {
|
||||
...DEFAULT_FLAGS,
|
||||
...fromStorage,
|
||||
};
|
||||
applyFlagsToDom(flags);
|
||||
}
|
||||
|
||||
initFlags();
|
||||
|
||||
export function getPerfProbeFlags(): PerfProbeFlags {
|
||||
return flags;
|
||||
}
|
||||
|
||||
export function subscribePerfProbeFlags(cb: () => void): () => void {
|
||||
listeners.add(cb);
|
||||
return () => listeners.delete(cb);
|
||||
}
|
||||
|
||||
export function setPerfProbeFlag<K extends keyof PerfProbeFlags>(key: K, value: PerfProbeFlags[K]): void {
|
||||
setFlags({ ...flags, [key]: value });
|
||||
}
|
||||
|
||||
export function resetPerfProbeFlags(): void {
|
||||
setFlags({ ...DEFAULT_FLAGS });
|
||||
}
|
||||
|
||||
export function usePerfProbeFlags(): PerfProbeFlags {
|
||||
return useSyncExternalStore(subscribePerfProbeFlags, getPerfProbeFlags, () => DEFAULT_FLAGS);
|
||||
}
|
||||
|
||||
/** Subscribe to a single probe flag so unrelated toggles do not re-render the consumer. */
|
||||
export function usePerfProbeFlag<K extends keyof PerfProbeFlags>(key: K): PerfProbeFlags[K] {
|
||||
return useSyncExternalStore(
|
||||
subscribePerfProbeFlags,
|
||||
() => getPerfProbeFlags()[key],
|
||||
() => DEFAULT_FLAGS[key],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Hot-path counters for the Performance Probe modal. In production builds these
|
||||
* no-op unless the probe is open (`data-psy-perf-probe-open` on the root), so
|
||||
* normal playback does not pay for object churn. Dev builds always record.
|
||||
*/
|
||||
const ROOT_FLAG = 'psyPerfProbeOpen';
|
||||
|
||||
export function setPerfProbeTelemetryActive(active: boolean): void {
|
||||
if (typeof document === 'undefined') return;
|
||||
if (active) document.documentElement.dataset[ROOT_FLAG] = 'true';
|
||||
else delete document.documentElement.dataset[ROOT_FLAG];
|
||||
}
|
||||
|
||||
function shouldRecordPerfCounters(): boolean {
|
||||
if (import.meta.env.DEV) return true;
|
||||
if (typeof document === 'undefined') return false;
|
||||
return document.documentElement.dataset[ROOT_FLAG] === 'true';
|
||||
}
|
||||
|
||||
export function bumpPerfCounter(name: string): void {
|
||||
if (!shouldRecordPerfCounters()) return;
|
||||
const w = globalThis as unknown as { __psyPerfCounters?: Record<string, number> };
|
||||
const c = w.__psyPerfCounters ?? (w.__psyPerfCounters = Object.create(null) as Record<string, number>);
|
||||
c[name] = (c[name] ?? 0) + 1;
|
||||
}
|
||||
Reference in New Issue
Block a user