mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-28 12:12:47 +00:00
* feat(plugins): play audio files on every client * fix(audio): preserve voice output and isolate waiting cues * fix(plugins): normalize audio MIME parameters * refactor(audio): share PCM conversion and keep plugin playback file-only
90 lines
2.9 KiB
TypeScript
90 lines
2.9 KiB
TypeScript
import { useVoiceAudioEngineOptional } from "@/contexts/voice-context";
|
|
import { pluginSettingsKey } from "./settings/use-settings";
|
|
import { useEffect } from "react";
|
|
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
|
import { useHostFeature } from "@/runtime/host-features";
|
|
import { useHostRuntimeIsConnected } from "@/runtime/host-runtime";
|
|
import { pluginRegistry } from "./registry";
|
|
|
|
export function PluginCatalogSync({
|
|
serverId,
|
|
client,
|
|
}: {
|
|
serverId: string;
|
|
client: DaemonClient;
|
|
}) {
|
|
const audio = useVoiceAudioEngineOptional();
|
|
const connected = useHostRuntimeIsConnected(serverId);
|
|
const supported = useHostFeature(serverId, "plugins");
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
let refreshQueue = Promise.resolve();
|
|
if (!supported || !audio) {
|
|
pluginRegistry.removeHost(serverId);
|
|
return;
|
|
}
|
|
if (!connected) {
|
|
pluginRegistry.removeHost(serverId);
|
|
return;
|
|
}
|
|
const refresh = (replacePluginId?: string) => {
|
|
refreshQueue = refreshQueue.then(() =>
|
|
client
|
|
.getPluginCatalog()
|
|
.then((catalog) => {
|
|
if (!cancelled) {
|
|
pluginRegistry.installCatalog(serverId, catalog, {
|
|
replacePluginId,
|
|
client,
|
|
audio,
|
|
});
|
|
}
|
|
return undefined;
|
|
})
|
|
.catch((error) => {
|
|
if (!cancelled) {
|
|
console.warn(`[Plugins] Failed to load catalog for ${serverId}`, error);
|
|
}
|
|
return undefined;
|
|
}),
|
|
);
|
|
return refreshQueue;
|
|
};
|
|
const observation = client.observeEvents([
|
|
"status.plugin_catalog_changed",
|
|
"status.plugin_settings_changed",
|
|
]);
|
|
observation.subscribe({
|
|
snapshot: () => {
|
|
void refresh();
|
|
},
|
|
update: (message) => {
|
|
if (message.type !== "status") return;
|
|
if (message.payload.status === "plugin_settings_changed") {
|
|
const { pluginId, settingsId } = message.payload;
|
|
if (typeof settingsId === "string") {
|
|
const plugin = pluginRegistry
|
|
.getSnapshot()
|
|
.find((item) => item.serverId === serverId && item.id === pluginId);
|
|
void plugin?.queryClient.invalidateQueries({ queryKey: pluginSettingsKey(settingsId) });
|
|
}
|
|
}
|
|
if (message.payload.status === "plugin_catalog_changed") {
|
|
const pluginId = message.payload.pluginId;
|
|
if (typeof pluginId === "string") void refresh(pluginId);
|
|
}
|
|
},
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
void observation
|
|
.release()
|
|
.catch((error) => console.warn("[Plugins] Failed to release catalog", error));
|
|
};
|
|
}, [audio, client, connected, serverId, supported]);
|
|
|
|
useEffect(() => () => pluginRegistry.removeHost(serverId), [serverId]);
|
|
return null;
|
|
}
|