Files
Psychotoxical-psysonic/src/app/MiniPlayerApp.tsx
T
Psychotoxical 7ad196711e refactor(lib): move generic hooks, DnD engine, shortcut contract to lib
Continue M4 lib/ de-flattening with domain-agnostic infra:
- lib/hooks/: 9 pure generic hooks (useDebouncedValue, useIsMobile,
  useLongPressAction, useRangeSelection, useResizeClientHeight,
  useWindowVisibility, useSystemPrefersDark, useVirtualizerScrollMargin,
  useRemeasureGridVirtualizer) — all PURE (only react/zustand/tanstack deps).
- lib/dnd/DragDropContext.tsx: the generic mouse-event DnD engine (WebKitGTK
  HTML5-DnD workaround, ~24 importers, self-contained) — empties src/contexts/.
- lib/shortcuts/: shortcutActions + shortcutTypes (the action-id + binding
  contract; registry/dispatch/bindings stay app-level and import the contract,
  app->lib direction).

useWindowFullscreenState deliberately NOT moved — kept in hooks/ as app-shell
per the handoff iron-rule list (overrides its pure-helper appearance).

Pure move via deep @/lib/* specifiers. tsc 0, lint 0/0, full suite 319 files /
2353 tests green.
2026-06-30 08:20:11 +02:00

49 lines
1.9 KiB
TypeScript

import { useEffect } from 'react';
import { DragDropProvider } from '@/lib/dnd/DragDropContext';
import MiniPlayer from '@/features/miniPlayer';
import GlobalConfirmModal from '../components/GlobalConfirmModal';
import TooltipPortal from '@/ui/TooltipPortal';
import FpsOverlay from '../components/FpsOverlay';
import { useThemeStore } from '../store/themeStore';
import { useFontStore } from '../store/fontStore';
import { useKeybindingsStore } from '../store/keybindingsStore';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import i18n from '@/lib/i18n';
/**
* Mini-player webview tree. Rendered in the secondary Tauri window labelled
* "mini" — no router, no sidebar, no full audio listeners. The window listens
* for state pushes from the main webview (via the storage event below) and
* sends control events back through the mini-player bridge.
*/
export default function MiniPlayerApp() {
const perfFlags = usePerfProbeFlags();
// Both webviews share localStorage (same origin), so the `storage` event
// fires in this window whenever main mutates a persisted key — but Zustand
// persist only reads localStorage on initial load, hence the explicit
// rehydrate.
useEffect(() => {
const onStorage = (e: StorageEvent) => {
if (!e.key) return;
if (e.key === 'psysonic_theme') useThemeStore.persist.rehydrate();
else if (e.key === 'psysonic_font') useFontStore.persist.rehydrate();
else if (e.key === 'psysonic_keybindings') useKeybindingsStore.persist.rehydrate();
else if (e.key === 'psysonic_language' && e.newValue) {
i18n.changeLanguage(e.newValue);
}
};
window.addEventListener('storage', onStorage);
return () => window.removeEventListener('storage', onStorage);
}, []);
return (
<DragDropProvider>
<MiniPlayer />
<GlobalConfirmModal />
{!perfFlags.disableTooltipPortal && <TooltipPortal />}
<FpsOverlay />
</DragDropProvider>
);
}