Refactor plugin management system
- Updated PluginLoader to utilize the new plugin marketplace manager for loading and managing plugins. - Simplified the plugin loading process by removing unnecessary state management and directly integrating with the plugin marketplace. - Enhanced error handling during plugin installation and uninstallation processes. - Removed legacy code related to Electron-specific plugin handling, streamlining the codebase for web compatibility. - Updated Plugins component to fetch marketplace plugins and installed plugins using the new plugin marketplace manager. - Refactored types related to plugins to import from the new plugin manager module, ensuring consistency and reducing redundancy. - Removed unused calling configuration from client settings and adjusted related types accordingly. - Cleaned up room state events by removing references to LiveKit service URLs, aligning with the updated architecture.
This commit is contained in:
@@ -24,21 +24,13 @@ import {
|
||||
import { HexColorPicker } from 'react-colorful';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||
import { PluginTab, PluginMetadata, PluginIndex, InstalledPlugin } from './types';
|
||||
import { PluginTab, PluginMetadata, InstalledPlugin } from './types';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { pluginRegistry, SettingsSchema, SettingDefinition } from './PluginAPI';
|
||||
import { pluginMarketplaceManager, pluginRegistry, SettingDefinition } from './PluginAPI';
|
||||
import { stopPropagation } from '../../../utils/keyboard';
|
||||
|
||||
const PLUGIN_INDEX_URL =
|
||||
'https://raw.githubusercontent.com/Paarrot/Plugin-Directory/refs/heads/main/plugins/index.json';
|
||||
const PLUGIN_BASE_URL =
|
||||
'https://raw.githubusercontent.com/Paarrot/Plugin-Directory/refs/heads/main/plugins/';
|
||||
|
||||
// Check if running in Electron
|
||||
const isElectron = typeof window !== 'undefined' && (window as any).electron?.plugins;
|
||||
|
||||
/**
|
||||
* Renders settings UI for a plugin based on its settings schema
|
||||
*/
|
||||
@@ -257,27 +249,21 @@ export function Plugins({ requestClose }: PluginsProps) {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [expandedSettings, setExpandedSettings] = useState<string | null>(null);
|
||||
|
||||
const refreshInstalledPlugins = useCallback(async () => {
|
||||
try {
|
||||
setInstalledPlugins(await pluginMarketplaceManager.listInstalledPlugins());
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to list installed plugins');
|
||||
console.error('Failed to list installed plugins:', err);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchMarketplacePlugins = useCallback(async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const indexResponse = await fetch(PLUGIN_INDEX_URL);
|
||||
if (!indexResponse.ok) {
|
||||
throw new Error('Failed to fetch plugin index');
|
||||
}
|
||||
const index: PluginIndex = await indexResponse.json();
|
||||
|
||||
const pluginPromises = index.plugins.map(async (pluginId) => {
|
||||
const pluginResponse = await fetch(`${PLUGIN_BASE_URL}${pluginId}.json`);
|
||||
if (!pluginResponse.ok) {
|
||||
console.error(`Failed to fetch plugin ${pluginId}`);
|
||||
return null;
|
||||
}
|
||||
return pluginResponse.json();
|
||||
});
|
||||
|
||||
const plugins = await Promise.all(pluginPromises);
|
||||
setMarketplacePlugins(plugins.filter((p): p is PluginMetadata => p !== null));
|
||||
setMarketplacePlugins(await pluginMarketplaceManager.fetchMarketplacePlugins());
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch plugins');
|
||||
console.error('Error fetching marketplace plugins:', err);
|
||||
@@ -293,112 +279,38 @@ export function Plugins({ requestClose }: PluginsProps) {
|
||||
}, [activeTab, fetchMarketplacePlugins]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadInstalledPlugins = async () => {
|
||||
if (isElectron) {
|
||||
// Load from filesystem via Electron
|
||||
try {
|
||||
const result = await (window as any).electron.plugins.list();
|
||||
if (result.success && result.data) {
|
||||
// Merge filesystem data with metadata from marketplace
|
||||
const fsPlugins = result.data.map((fsPlugin: any) => ({
|
||||
...fsPlugin,
|
||||
enabled: true, // Default to enabled
|
||||
}));
|
||||
setInstalledPlugins(fsPlugins);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to list plugins from filesystem:', err);
|
||||
}
|
||||
} else {
|
||||
// Fallback to localStorage for web
|
||||
const stored = localStorage.getItem('installedPlugins');
|
||||
if (stored) {
|
||||
try {
|
||||
setInstalledPlugins(JSON.parse(stored));
|
||||
} catch (err) {
|
||||
console.error('Failed to parse installed plugins:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadInstalledPlugins();
|
||||
}, []);
|
||||
refreshInstalledPlugins();
|
||||
}, [refreshInstalledPlugins]);
|
||||
|
||||
const handleInstall = useCallback(
|
||||
async (plugin: PluginMetadata) => {
|
||||
if (isElectron) {
|
||||
// Download and install to filesystem via Electron
|
||||
try {
|
||||
setLoading(true);
|
||||
const result = await (window as any).electron.plugins.download(
|
||||
plugin.id,
|
||||
plugin.downloadUrl,
|
||||
plugin.name
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
const newPlugin: InstalledPlugin = {
|
||||
...plugin,
|
||||
installedDate: new Date().toISOString(),
|
||||
enabled: true,
|
||||
};
|
||||
setInstalledPlugins([...installedPlugins, newPlugin]);
|
||||
} else {
|
||||
setError(result.error || 'Failed to install plugin');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to install plugin:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to install plugin');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
// Fallback to localStorage for web
|
||||
const newPlugin: InstalledPlugin = {
|
||||
...plugin,
|
||||
installedDate: new Date().toISOString(),
|
||||
enabled: true,
|
||||
};
|
||||
const updated = [...installedPlugins, newPlugin];
|
||||
setInstalledPlugins(updated);
|
||||
localStorage.setItem('installedPlugins', JSON.stringify(updated));
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
await pluginMarketplaceManager.installPlugin(plugin);
|
||||
await refreshInstalledPlugins();
|
||||
} catch (err) {
|
||||
console.error('Failed to install plugin:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to install plugin');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[installedPlugins]
|
||||
[refreshInstalledPlugins]
|
||||
);
|
||||
|
||||
const handleUninstall = useCallback(
|
||||
async (pluginId: string) => {
|
||||
// First, unregister the plugin to trigger onUnload
|
||||
try {
|
||||
await pluginRegistry.unregisterPlugin(pluginId);
|
||||
setError(null);
|
||||
await pluginMarketplaceManager.uninstallPlugin(pluginId);
|
||||
await refreshInstalledPlugins();
|
||||
} catch (err) {
|
||||
console.error(`Failed to unregister plugin ${pluginId}:`, err);
|
||||
}
|
||||
|
||||
if (isElectron) {
|
||||
// Uninstall from filesystem via Electron
|
||||
try {
|
||||
const result = await (window as any).electron.plugins.uninstall(pluginId);
|
||||
if (result.success) {
|
||||
const updated = installedPlugins.filter((p) => p.id !== pluginId);
|
||||
setInstalledPlugins(updated);
|
||||
} else {
|
||||
setError(result.error || 'Failed to uninstall plugin');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to uninstall plugin:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to uninstall plugin');
|
||||
}
|
||||
} else {
|
||||
// Fallback to localStorage for web
|
||||
const updated = installedPlugins.filter((p) => p.id !== pluginId);
|
||||
setInstalledPlugins(updated);
|
||||
localStorage.setItem('installedPlugins', JSON.stringify(updated));
|
||||
console.error('Failed to uninstall plugin:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to uninstall plugin');
|
||||
}
|
||||
},
|
||||
[installedPlugins]
|
||||
[refreshInstalledPlugins]
|
||||
);
|
||||
|
||||
const handleToggleEnabled = useCallback(
|
||||
@@ -408,36 +320,19 @@ export function Plugins({ requestClose }: PluginsProps) {
|
||||
|
||||
const newEnabledState = !plugin.enabled;
|
||||
|
||||
if (plugin.enabled) {
|
||||
// Disabling: unregister the plugin to trigger onUnload
|
||||
try {
|
||||
await pluginRegistry.unregisterPlugin(pluginId);
|
||||
console.log(`[Plugins] Plugin ${pluginId} disabled and unloaded`);
|
||||
} catch (err) {
|
||||
console.error(`Failed to unregister plugin ${pluginId}:`, err);
|
||||
try {
|
||||
setError(null);
|
||||
await pluginMarketplaceManager.setPluginEnabled(pluginId, newEnabledState);
|
||||
await refreshInstalledPlugins();
|
||||
if (newEnabledState) {
|
||||
console.log(`[Plugins] Plugin ${pluginId} enabled. Restart app to load it.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update installedPlugins list
|
||||
const updated = installedPlugins.map((p) =>
|
||||
p.id === pluginId ? { ...p, enabled: newEnabledState } : p
|
||||
);
|
||||
setInstalledPlugins(updated);
|
||||
localStorage.setItem('installedPlugins', JSON.stringify(updated));
|
||||
|
||||
// Also update enabledPlugins for PluginLoader
|
||||
const enabledPluginsStr = localStorage.getItem('enabledPlugins');
|
||||
const enabledPlugins = enabledPluginsStr ? JSON.parse(enabledPluginsStr) : {};
|
||||
enabledPlugins[pluginId] = newEnabledState;
|
||||
localStorage.setItem('enabledPlugins', JSON.stringify(enabledPlugins));
|
||||
|
||||
// Note: Enabling a plugin requires app restart to load it
|
||||
// PluginLoader only loads plugins on mount
|
||||
if (newEnabledState) {
|
||||
console.log(`[Plugins] Plugin ${pluginId} enabled. Restart app to load it.`);
|
||||
} catch (err) {
|
||||
console.error(`Failed to toggle plugin ${pluginId}:`, err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to update plugin state');
|
||||
}
|
||||
},
|
||||
[installedPlugins]
|
||||
[installedPlugins, refreshInstalledPlugins]
|
||||
);
|
||||
|
||||
const isPluginInstalled = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user