feat: present asset plugins as control cards
This commit is contained in:
@@ -23,6 +23,13 @@ function draftFromPlugin(plugin: AssetPluginConfig): PluginDraft {
|
||||
};
|
||||
}
|
||||
|
||||
const pluginVisuals: Record<string, { icon: string; accent: string }> = {
|
||||
'asset-search': { icon: '⌕', accent: 'blue' },
|
||||
'asset-generate': { icon: '✦', accent: 'violet' },
|
||||
'asset-transform': { icon: '✧', accent: 'amber' },
|
||||
'asset-analyze': { icon: '◉', accent: 'green' },
|
||||
};
|
||||
|
||||
export function AssetGatewayPage() {
|
||||
const [config, setConfig] = useState<AssetGatewayConfig | null>(null);
|
||||
const [keys, setKeys] = useState<LlmProviderKeyRow[]>([]);
|
||||
@@ -93,37 +100,51 @@ export function AssetGatewayPage() {
|
||||
</div>
|
||||
{error && <p className="banner banner-error">{error}</p>}
|
||||
{message && <p className="banner banner-success">{message}</p>}
|
||||
<section className="settings-card">
|
||||
<h3>全局总开关</h3>
|
||||
<label><input type="checkbox" checked={config.enabled} disabled={busy === 'global'} onChange={(event) => void saveGlobal(event.target.checked)} /> 启用资产能力 Gateway</label>
|
||||
<p className="muted">关闭时,所有插件安全降级为原有流程;插件配置将被保留。</p>
|
||||
<section className="asset-gateway-hero">
|
||||
<div>
|
||||
<div className="asset-kicker">OPTIONAL CAPABILITY</div>
|
||||
<h3>资产能力 Gateway</h3>
|
||||
<p>关闭时所有调用自动降级为现有流程;配置保留,现有聊天和页面生成不受影响。</p>
|
||||
</div>
|
||||
<label className="asset-toggle">
|
||||
<input type="checkbox" checked={config.enabled} disabled={busy === 'global'} onChange={(event) => void saveGlobal(event.target.checked)} />
|
||||
<span>{config.enabled ? '已启用' : '已关闭'}</span>
|
||||
</label>
|
||||
</section>
|
||||
<div className="asset-plugin-grid">
|
||||
{config.plugins.map((plugin) => {
|
||||
const draft = drafts[plugin.pluginId] ?? draftFromPlugin(plugin);
|
||||
const selectedKey = activeKeys.find((key) => key.id === draft.llmProviderKeyId);
|
||||
return <section className="settings-card" key={plugin.pluginId}>
|
||||
<h3>{plugin.label}</h3>
|
||||
<p className="muted">{plugin.description}</p>
|
||||
<div className="executor-form-grid">
|
||||
<label><input type="checkbox" checked={draft.enabled} onChange={(event) => updateDraft(plugin.pluginId, { enabled: event.target.checked })} /> 启用插件</label>
|
||||
<select value={draft.provider} onChange={(event) => updateDraft(plugin.pluginId, { provider: event.target.value })} disabled={!draft.enabled}>
|
||||
const visual = pluginVisuals[plugin.pluginId] ?? { icon: '✦', accent: 'blue' };
|
||||
return <section className={`asset-plugin-card asset-plugin-card--${visual.accent}`} key={plugin.pluginId}>
|
||||
<div className="asset-plugin-card-head">
|
||||
<div className="asset-plugin-icon" aria-hidden="true">{visual.icon}</div>
|
||||
<div><h3>{plugin.label}</h3><p>{plugin.description}</p></div>
|
||||
<label className="asset-mini-toggle" title={`启用${plugin.label}`}>
|
||||
<input type="checkbox" checked={draft.enabled} onChange={(event) => updateDraft(plugin.pluginId, { enabled: event.target.checked })} />
|
||||
<span>{draft.enabled ? '开启' : '关闭'}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="asset-plugin-fields">
|
||||
<label>运行 Provider<select value={draft.provider} onChange={(event) => updateDraft(plugin.pluginId, { provider: event.target.value })} disabled={!draft.enabled}>
|
||||
<option value="">选择 Provider</option>
|
||||
{plugin.providers.map((provider) => <option key={provider} value={provider}>{provider}</option>)}
|
||||
</select>
|
||||
</select></label>
|
||||
{plugin.supportsLlm ? <>
|
||||
<select value={draft.llmProviderKeyId} onChange={(event) => updateDraft(plugin.pluginId, { llmProviderKeyId: event.target.value, llmModel: '' })} disabled={!draft.enabled}>
|
||||
<label>专属 LLM<select value={draft.llmProviderKeyId} onChange={(event) => updateDraft(plugin.pluginId, { llmProviderKeyId: event.target.value, llmModel: '' })} disabled={!draft.enabled}>
|
||||
<option value="">不使用 LLM</option>
|
||||
{activeKeys.map((key) => <option key={key.id} value={key.id}>{key.name}</option>)}
|
||||
</select>
|
||||
<select value={draft.llmModel} onChange={(event) => updateDraft(plugin.pluginId, { llmModel: event.target.value })} disabled={!draft.enabled || !selectedKey}>
|
||||
</select></label>
|
||||
<label>模型<select value={draft.llmModel} onChange={(event) => updateDraft(plugin.pluginId, { llmModel: event.target.value })} disabled={!draft.enabled || !selectedKey}>
|
||||
<option value="">选择模型</option>
|
||||
{selectedKey?.models.map((model) => <option key={model} value={model}>{model}</option>)}
|
||||
</select>
|
||||
</> : <p className="muted">此插件不调用 LLM。</p>}
|
||||
<button type="button" className="send-btn" disabled={busy === plugin.pluginId} onClick={() => void savePlugin(plugin)}>{busy === plugin.pluginId ? '保存中…' : '保存插件'}</button>
|
||||
</select></label>
|
||||
</> : <div className="asset-no-llm">确定性处理 · 不调用 LLM</div>}
|
||||
</div>
|
||||
<div className="asset-plugin-footer"><span className={`asset-status ${draft.enabled ? 'is-on' : ''}`}>{draft.enabled ? '待保存为启用' : '默认安全关闭'}</span><button type="button" className="send-btn" disabled={busy === plugin.pluginId} onClick={() => void savePlugin(plugin)}>{busy === plugin.pluginId ? '保存中…' : '保存配置'}</button></div>
|
||||
</section>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user