A plugin's update diff now includes its marketplace.json entry as a pretty-printed "marketplace.json entry" file, so inline hooks, MCP servers and commands are reviewed like any file. CatalogItem gains plugin_components (entry / plugin.json runnable keys, hooks/hooks.json, .mcp.json, commands/), and installing a plugin now goes through PluginConfirmModal listing them. The import-preview warnings describe that confirmation accurately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import type { MarketplaceApi } from "../../hooks/useMarketplace";
|
||||
import type { CatalogItem, InstallScope, MarketplaceItemRef } from "../../lib/types";
|
||||
import Toggle from "../ui/Toggle";
|
||||
import HookConfirmModal from "./HookConfirmModal";
|
||||
import PluginConfirmModal from "./PluginConfirmModal";
|
||||
|
||||
const STATE_LABEL: Record<ProjectItemState, string> = {
|
||||
none: "",
|
||||
@@ -25,8 +26,8 @@ interface Props {
|
||||
headCommit: string | null;
|
||||
}
|
||||
|
||||
/** A hook install waiting for confirmation, frozen at the moment it was asked for. */
|
||||
interface PendingHook {
|
||||
/** A hook or plugin install waiting for confirmation, frozen at the moment it was asked for. */
|
||||
interface PendingConfirm {
|
||||
scope: InstallScope;
|
||||
item: CatalogItem;
|
||||
commit: string;
|
||||
@@ -36,7 +37,7 @@ export default function InstallControls({ mp, item, marketplaceId, headCommit }:
|
||||
const appSettings = useAppState((s) => s.appSettings);
|
||||
const projects = useAppState((s) => s.projects);
|
||||
const filterId = useAppState((s) => s.marketplaceFilterProjectId);
|
||||
const [pendingHook, setPendingHook] = useState<PendingHook | null>(null);
|
||||
const [pending, setPending] = useState<PendingConfirm | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const ref: MarketplaceItemRef = { marketplace_id: marketplaceId, kind: item.kind, key: item.key };
|
||||
@@ -58,10 +59,10 @@ export default function InstallControls({ mp, item, marketplaceId, headCommit }:
|
||||
}
|
||||
};
|
||||
|
||||
/** Every install goes through here so a hook is always confirmed first. */
|
||||
/** Every install goes through here so a hook or plugin is always confirmed first. */
|
||||
const install = (scope: InstallScope) => {
|
||||
if (item.kind === "hook") {
|
||||
setPendingHook({ scope, item, commit });
|
||||
if (item.kind === "hook" || item.kind === "plugin") {
|
||||
setPending({ scope, item, commit });
|
||||
return;
|
||||
}
|
||||
void run(() => mp.install(ref, scope, commit));
|
||||
@@ -123,18 +124,23 @@ export default function InstallControls({ mp, item, marketplaceId, headCommit }:
|
||||
{projects.length === 0 && (
|
||||
<p className="text-xs text-[var(--text-secondary)]">No projects yet — “All projects” also covers projects added later.</p>
|
||||
)}
|
||||
{pendingHook && (
|
||||
<HookConfirmModal
|
||||
item={pendingHook.item}
|
||||
commit={pendingHook.commit}
|
||||
onCancel={() => setPendingHook(null)}
|
||||
onConfirm={() => {
|
||||
const { scope, commit: reviewed } = pendingHook;
|
||||
setPendingHook(null);
|
||||
{pending &&
|
||||
(() => {
|
||||
const confirm = () => {
|
||||
const { scope, commit: reviewed } = pending;
|
||||
setPending(null);
|
||||
void run(() => mp.install(ref, scope, reviewed));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
};
|
||||
const Confirm = pending.item.kind === "plugin" ? PluginConfirmModal : HookConfirmModal;
|
||||
return (
|
||||
<Confirm
|
||||
item={pending.item}
|
||||
commit={pending.commit}
|
||||
onCancel={() => setPending(null)}
|
||||
onConfirm={confirm}
|
||||
/>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user