feat(plugins): custom splashes

This commit is contained in:
neoapps-dev
2026-08-27 17:14:56 +03:00
parent 683339ae4e
commit c4b87009be
4 changed files with 53 additions and 6 deletions
+13 -6
View File
@@ -1,5 +1,5 @@
import { useState, useEffect, useRef, useCallback } from "react";
import { SPLASHES } from "../data/splashes";
import { PluginManager } from "../plugins/PluginManager";
const TRACKS = [
"music/Moog City 2.opus",
@@ -29,10 +29,16 @@ export function useAudioController({
isGameRunning,
isWindowVisible,
}: AudioControllerProps) {
const [splashes, setSplashes] = useState<string[]>(() => PluginManager.instance.getSplashes());
const [currentTrack, setCurrentTrack] = useState(0);
const [splashIndex, setSplashIndex] = useState(
() => Math.floor(Math.random() * SPLASHES.length),
() => Math.floor(Math.random() * PluginManager.instance.getSplashes().length),
);
useEffect(() => {
PluginManager.instance.setSplashesChangedCallback((s) => setSplashes(s));
setSplashes(PluginManager.instance.getSplashes());
}, []);
const audioContextRef = useRef<AudioContext | null>(null);
const musicSourceRef = useRef<AudioBufferSourceNode | null>(null);
const musicGainRef = useRef<GainNode | null>(null);
@@ -235,11 +241,12 @@ export function useAudioController({
const cycleSplash = useCallback(() => {
playSplashSound();
let newIndex;
if (splashes.length === 0) return;
do {
newIndex = Math.floor(Math.random() * SPLASHES.length);
} while (newIndex === splashIndex && SPLASHES.length > 1);
newIndex = Math.floor(Math.random() * splashes.length);
} while (newIndex === splashIndex && splashes.length > 1);
setSplashIndex(newIndex);
}, [playSplashSound, splashIndex]);
}, [playSplashSound, splashIndex, splashes]);
const skipTrack = useCallback(() => {
isManualSkipRef.current = true;
setCurrentTrack((prev) => (prev + 1) % TRACKS.length);
@@ -346,7 +353,7 @@ export function useAudioController({
playBackSound,
playSfx,
tracks: TRACKS,
splashes: SPLASHES,
splashes: splashes,
startMusic,
};
}
+3
View File
@@ -189,6 +189,9 @@ export function buildPluginAPI(
playSound(name: string): void {
manager.playSound(name);
},
addSplash(splash: string): UnsubscribeFn {
return manager.registerSplash(pluginId, splash);
},
async openUrl(url: string): Promise<void> {
if (
perms.size > 0 &&
+36
View File
@@ -1,6 +1,7 @@
import { invoke } from "@tauri-apps/api/core";
import { buildPluginAPI } from "./PluginAPI";
import { PluginSandbox } from "./PluginSandbox";
import { SPLASHES } from "../data/splashes";
import type {
PluginManifest,
LoadedPlugin,
@@ -39,12 +40,14 @@ export class PluginManager {
private hooks: Map<HookEvent, Map<string, HookCallback>> = new Map();
private views: Map<string, PluginViewRegistration> = new Map();
private actions: Map<ActionSlot, Map<string, { action: ActionDef; pluginId: string }>> = new Map();
private splashes: Map<string, Set<string>> = new Map();
private stateSubs: Map<string, StateChangeCallback> = new Map();
private pluginEvents: Map<string, Map<string, Set<PluginEventHandler>>> = new Map();
private onViewsChanged: ViewChangeCallback | null = null;
private onNavigate: NavigateCallback | null = null;
private onToast: ToastCallback | null = null;
private onSound: SoundCallback | null = null;
private onSplashesChanged: ((splashes: string[]) => void) | null = null;
private onEnabledChanged: (() => void) | null = null;
private configSnapshot: Record<string, unknown> = {};
private gameStateSnapshot: Record<string, unknown> = {};
@@ -71,6 +74,7 @@ export class PluginManager {
this.enabledMap.set(id, enabled);
localStorage.setItem(`plugin:enabled:${id}`, String(enabled));
this.notifyViewsChanged();
this.notifySplashesChanged();
this.onEnabledChanged?.();
}
@@ -97,6 +101,10 @@ export class PluginManager {
this.onSound = cb;
}
setSplashesChangedCallback(cb: (splashes: string[]) => void): void {
this.onSplashesChanged = cb;
}
updateSnapshots(
config: Record<string, unknown>,
game: Record<string, unknown>,
@@ -139,9 +147,11 @@ export class PluginManager {
this.hooks.clear();
this.views.clear();
this.actions.clear();
this.splashes.clear();
this.plugins.clear();
this.enabledMap.clear();
this.pluginEvents.clear();
this.notifySplashesChanged();
await this.init();
}
@@ -369,7 +379,33 @@ export class PluginManager {
this.onSound?.(name);
}
registerSplash(pluginId: string, splash: string): UnsubscribeFn {
if (!this.splashes.has(pluginId)) {
this.splashes.set(pluginId, new Set());
}
this.splashes.get(pluginId)!.add(splash);
this.notifySplashesChanged();
return () => {
this.splashes.get(pluginId)?.delete(splash);
this.notifySplashesChanged();
};
}
getSplashes(): string[] {
const pluginSplashes: string[] = [];
this.splashes.forEach((set, pid) => {
if (this.isPluginEnabled(pid)) {
pluginSplashes.push(...set);
}
});
return [...SPLASHES, ...pluginSplashes];
}
private notifyViewsChanged(): void {
this.onViewsChanged?.(this.getViews());
}
private notifySplashesChanged(): void {
this.onSplashesChanged?.(this.getSplashes());
}
}
+1
View File
@@ -134,6 +134,7 @@ export interface PluginAPI {
ui: {
showToast(message: string, options?: ToastOptions): void;
playSound(name: string): void;
addSplash(splash: string): UnsubscribeFn;
openUrl(url: string): Promise<void>;
};
log: {