'use client' import { useContext, useEffect, useRef, useState } from 'react' import { ChartPieIcon, CommandLineIcon, PlayIcon, } from '@heroicons/react/24/outline' import { MagnifyingGlassIcon } from '@heroicons/react/20/solid' import classNames from 'classnames' import LoadingIndicator from './LoadingIndicator' import { FeatureToggleContext } from '@helpers/FeatureToggleWrapper' import { pluginManager } from '@plugin/PluginManager' export const Preferences = () => { const [search, setSearch] = useState('') const [activePlugins, setActivePlugins] = useState([]) const [preferenceItems, setPreferenceItems] = useState([]) const [preferenceValues, setPreferenceValues] = useState([]) const [isTestAvailable, setIsTestAvailable] = useState(false) const [fileName, setFileName] = useState('') const [pluginCatalog, setPluginCatalog] = useState([]) const [isLoading, setIsLoading] = useState(false) const experimentRef = useRef(null) const preferenceRef = useRef(null) const { experimentalFeatureEnabed } = useContext(FeatureToggleContext) /** * Loads the plugin catalog module from a CDN and sets it as the plugin catalog state. */ useEffect(() => { if (!window.electronAPI) { return } // Get plugin manifest import(/* webpackIgnore: true */ PLUGIN_CATALOG + `?t=${Date.now()}`).then( (data) => { if (Array.isArray(data.default) && experimentalFeatureEnabed) setPluginCatalog(data.default) } ) }, []) /** * Fetches the active plugins and their preferences from the `plugins` and `preferences` modules. * If the `experimentComponent` extension point is available, it executes the extension point and * appends the returned components to the `experimentRef` element. * If the `PluginPreferences` extension point is available, it executes the extension point and * fetches the preferences for each plugin using the `preferences.get` function. */ useEffect(() => { const getActivePlugins = async () => { const plgs = await pluginManager.getActive() setActivePlugins(plgs) } getActivePlugins() }, []) /** * Installs a plugin by calling the `plugins.install` function with the plugin file path. * If the installation is successful, the application is relaunched using the `coreAPI.relaunch` function. * @param e - The event object. */ const install = async (e: any) => { e.preventDefault() //@ts-ignore const pluginFile = new FormData(e.target).get('plugin-file').path // Send the filename of the to be installed plugin // to the main process for installation const installed = await pluginManager.install([pluginFile]) if (installed) window.coreAPI?.relaunch() } /** * Uninstalls a plugin by calling the `plugins.uninstall` function with the plugin name. * If the uninstallation is successful, the application is relaunched using the `coreAPI.relaunch` function. * @param name - The name of the plugin to uninstall. */ const uninstall = async (name: string) => { // Send the filename of the to be uninstalled plugin // to the main process for removal const res = await pluginManager.uninstall([name]) if (res) window.coreAPI?.relaunch() } /** * Updates a plugin by calling the `window.pluggableElectronIpc.update` function with the plugin name. * If the update is successful, the application is relaunched using the `window.coreAPI.relaunch` function. * TODO: should update using window.coreAPI rather than pluggableElectronIpc (Plugin Manager Facades) * @param plugin - The name of the plugin to update. */ const update = async (plugin: string) => { if (typeof window !== 'undefined') { // @ts-ignore await window.pluggableElectronIpc.update([plugin], true) window.coreAPI?.relaunch() } } /** * Downloads a remote plugin tarball and installs it using the `plugins.install` function. * If the installation is successful, the application is relaunched using the `coreAPI.relaunch` function. * @param pluginName - The name of the remote plugin to download and install. */ const downloadTarball = async (pluginName: string) => { setIsLoading(true) const pluginPath = await window.coreAPI?.installRemotePlugin(pluginName) const installed = await pluginManager.install([pluginPath]) setIsLoading(false) if (installed) window.coreAPI.relaunch() } /** * Notifies plugins of a preference update by executing the `PluginService.OnPreferencesUpdate` event. * If a timeout is already set, it is cleared before setting a new timeout to execute the event. */ let timeout: any | undefined = undefined function notifyPreferenceUpdate() { if (timeout) { clearTimeout(timeout) } } /** * Handles the change event of the plugin file input element by setting the file name state. * Its to be used to display the plugin file name of the selected file. * @param event - The change event object. */ const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (file) { setFileName(file.name) } else { setFileName('') } } return (
{/* Separator */}
{/* Content */}
Install Plugin
Installed Plugins
{activePlugins .filter( (e) => search.trim() === '' || e.name.toLowerCase().includes(search.toLowerCase()) ) .map((e) => (

{e.name}

Version: {e.version}

{e.description ?? "Jan's Plugin"}

))}
Explore Plugins
{pluginCatalog .filter( (e: any) => search.trim() === '' || e.name.toLowerCase().includes(search.toLowerCase()) ) .map((e: any) => (

{e.name}

Version: {e.version}

{e.description ?? "Jan's Plugin"}

{e.version !== activePlugins.filter((p) => p.name === e.name)[0] ?.version && ( )}
))}
{activePlugins.length > 0 && isTestAvailable && (
Test Plugins
)}
Preferences
{preferenceItems?.map((e) => (
Setting:{' '} {e.preferenceName}
{e.preferenceDescription}
v.key === e.preferenceKey )[0]?.value } onChange={(event) => {}} >
))}
{isLoading && (
Installing...
)}
) }