feat(plugins): 插件管理 - 区分系统与官方插件权限 (#329)

This commit is contained in:
MasterChiefCN
2026-08-27 11:22:00 +08:00
committed by GitHub
parent 23b4628c81
commit f111d57fc6
32 changed files with 1137 additions and 143 deletions

View File

@@ -262,11 +262,12 @@ export function CanvasNodeContextMenu({
function AddNodeContextMenu({ parentPosition, workspaceMode, isProjectLinked, reducedMotion, onAddNode, onAddFolder, onChooseStyle, onOpenDirector, onUpload, onOpenAssets, onOpenProjectCharacters }: { parentPosition: { left: number; top: number }; workspaceMode: CanvasWorkspaceMode; isProjectLinked: boolean; reducedMotion: boolean; onAddNode: (type: CanvasNodeTypeId) => void; onAddFolder: () => void; onChooseStyle: () => void; onOpenDirector: () => void; onUpload: () => void; onOpenAssets: () => void; onOpenProjectCharacters: () => void }) {
const theme = canvasThemes[useThemeStore((state) => state.theme)];
const installations = usePluginStore((state) => state.installations);
const pluginStates = usePluginStore((state) => state.pluginStates);
const left = getSubmenuLeft(parentPosition.left);
const createContext: AddNodeMenuContext = {
workspaceMode,
isProjectLinked,
enabledPluginIds: new Set(installations.filter((item) => item.enabled).map((item) => item.manifest.id)),
enabledPluginIds: new Set(installations.filter((item) => pluginStates[item.manifest.id]?.effectiveEnabled ?? item.enabled).map((item) => item.manifest.id)),
handlers: {
onAddText: () => onAddNode(CanvasNodeType.Text),
onAddImage: () => onAddNode(CanvasNodeType.Image),

View File

@@ -59,6 +59,7 @@ export function CanvasNodePromptPanel({ node, isRunning, onPromptChange, onConfi
const theme = canvasThemes[themeName];
const creditsEnabled = useUserStore((state) => state.features.creditsEnabled);
const promptOptimizerInstallation = usePluginStore((state) => state.installations.find((item) => item.manifest.id === PROMPT_OPTIMIZER_PLUGIN_ID));
const promptOptimizerEnabled = usePluginStore((state) => state.pluginStates[PROMPT_OPTIMIZER_PLUGIN_ID]?.effectiveEnabled ?? Boolean(state.installations.find((item) => item.manifest.id === PROMPT_OPTIMIZER_PLUGIN_ID)?.enabled));
const simpleMode = workspaceMode === "simple";
const mode = defaultMode(node.type);
const hasTextContent = node.type === CanvasNodeType.Text && Boolean(node.metadata?.content?.trim());
@@ -102,9 +103,9 @@ export function CanvasNodePromptPanel({ node, isRunning, onPromptChange, onConfi
};
const config = buildNodeConfig(globalConfig, node, mode, requirements);
const promptOptimizerProvider = useMemo(() => {
if (!promptOptimizerInstallation?.enabled || !promptOptimizerPlugin.createPromptOptimizer) return null;
if (!promptOptimizerEnabled || !promptOptimizerInstallation || !promptOptimizerPlugin.createPromptOptimizer) return null;
return promptOptimizerPlugin.createPromptOptimizer(createPluginHostContext(promptOptimizerPlugin, promptOptimizerInstallation, globalConfig));
}, [globalConfig, promptOptimizerInstallation]);
}, [globalConfig, promptOptimizerEnabled, promptOptimizerInstallation]);
const generationCount = Math.max(1, Math.min(15, Math.floor(Math.abs(Number(config.count)) || 1)));
const priceChannel = resolveModelChannel(config, config.model);
const credits = requestCreditCost({

View File

@@ -85,6 +85,7 @@ export function CanvasToolbar({
const dockRef = useRef<HTMLDivElement>(null);
const colorTheme = useThemeStore((state) => state.theme);
const installations = usePluginStore((state) => state.installations);
const pluginStates = usePluginStore((state) => state.pluginStates);
const setTheme = useThemeStore((state) => state.setTheme);
const theme = canvasThemes[colorTheme];
const [addOpen, setAddOpen] = useState(false);
@@ -173,7 +174,7 @@ export function CanvasToolbar({
handlers,
};
const enabledPluginIds = new Set(installations.filter((item) => item.enabled).map((item) => item.manifest.id));
const enabledPluginIds = new Set(installations.filter((item) => pluginStates[item.manifest.id]?.effectiveEnabled ?? item.enabled).map((item) => item.manifest.id));
const items = resolveToolbarEntries("main", ctx, prefs ?? defaultToolbarPrefs("main"));

View File

@@ -23,11 +23,10 @@ const roleLabels: Record<PortraitClearanceInputRole, string> = {
export function PortraitClearanceNodeContent({ node }: PortraitClearanceNodeProps) {
const theme = canvasThemes[useThemeStore((state) => state.theme)];
const installations = usePluginStore((state) => state.installations);
const enabled = usePluginStore((state) => state.pluginStates[PORTRAIT_CLEARANCE_PLUGIN_ID]?.effectiveEnabled ?? Boolean(state.installations.find((item) => item.manifest.id === PORTRAIT_CLEARANCE_PLUGIN_ID)?.enabled));
const { openPortraitClearance } = useCanvasNodeActions();
const upstream = useUpstreamNodes(node.id).filter((item) => isPortraitImageInput(item));
const state = node.metadata?.portraitClearance;
const enabled = Boolean(installations.find((item) => item.manifest.id === PORTRAIT_CLEARANCE_PLUGIN_ID)?.enabled);
const bindings = state?.inputBindings || [];
const roleByNodeId = new Map(bindings.map((binding) => [binding.nodeId, binding.role]));
const query = upstream.find((item) => roleByNodeId.get(item.id) === "query") || upstream[0];

View File

@@ -22,9 +22,8 @@ type PortraitClearanceModalProps = {
};
export function PortraitClearanceModal({ projectId, node, upstreamNodes, open, onClose, onUpdateState, onAddCandidate }: PortraitClearanceModalProps) {
const installations = usePluginStore((state) => state.installations);
const effectiveConfig = useEffectiveConfig();
const enabled = Boolean(installations.find((item) => item.manifest.id === PORTRAIT_CLEARANCE_PLUGIN_ID)?.enabled);
const enabled = usePluginStore((state) => state.pluginStates[PORTRAIT_CLEARANCE_PLUGIN_ID]?.effectiveEnabled ?? Boolean(state.installations.find((item) => item.manifest.id === PORTRAIT_CLEARANCE_PLUGIN_ID)?.enabled));
const state = node?.metadata?.portraitClearance || createDefaultPortraitClearanceState();
const [starting, setStarting] = useState(false);
const [installingModels, setInstallingModels] = useState(false);