"use client"; import { useState } from "react"; import { IconRefresh } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@kandev/ui/tabs"; import { SettingsPageTemplate } from "@/components/settings/settings-page-template"; import { usePlugins } from "@/hooks/domains/plugins/use-plugins"; import { usePluginUpdates } from "@/hooks/domains/plugins/use-plugin-updates"; import type { MarketplaceEntry } from "@/lib/types/plugins"; import { InstallPluginDialog } from "./install-plugin-dialog"; import { MarketplaceBrowser } from "./marketplace-browser"; import { PluginRow } from "./plugin-row"; import { UninstallPluginDialog } from "./uninstall-plugin-dialog"; import { usePluginActions } from "./use-plugin-actions"; /** * Operator UI to browse, install, enable, disable, uninstall, and update kandev * plugins (docs/specs/plugins/marketplace.md). Gated on the `plugins` feature * flag by the page-level default export. */ export function PluginsSettings() { const list = usePlugins(); const actions = usePluginActions(); const { updates, reload: reloadUpdates } = usePluginUpdates(); const [updatingId, setUpdatingId] = useState(null); // Update = install the newer package over the current one (marketplaceInstall // upserts the new record into the store, so the row's version refreshes), // then re-check the catalog so the resolved update drops off the row. const handleUpdate = async (entry: MarketplaceEntry) => { setUpdatingId(entry.id); try { await actions.marketplaceInstall(entry.package_url); reloadUpdates(); } finally { setUpdatingId(null); } }; return ( undefined} showSaveButton={false} > Installed Browse ); } type InstalledTabProps = { list: ReturnType; actions: ReturnType; updates: Map; updatingId: string | null; onUpdate: (entry: MarketplaceEntry) => void; }; /** The Installed tab: sync/install toolbar, sync errors, and the plugin list. */ function InstalledTab({ list, actions, updates, updatingId, onUpdate }: InstalledTabProps) { return ( <>
Installed plugins
{actions.syncErrors.length > 0 && (
{actions.syncErrors.map((err) => (
{err.path}: {err.reason}
))}
)} ); } type PluginListProps = { list: ReturnType; actions: ReturnType; updates: Map; updatingId: string | null; onUpdate: (entry: MarketplaceEntry) => void; }; function PluginList({ list, actions, updates, updatingId, onUpdate }: PluginListProps) { const { items, loaded, loading, error } = list; if (error) { return (
{error}
); } if (!loaded && loading) { return (
Loading plugins...
); } if (loaded && items.length === 0) { return (
No plugins yet. Install one from the Browse tab, or upload a package.
); } return (
{items.map((plugin) => ( ))}
); }