feat: make project scaffold project-wide
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
import type { DataServiceInstanceState } from '../../../shared/data-service';
|
||||
import {
|
||||
isBundledOfficialPlugin,
|
||||
isProjectWideOfficialPlugin,
|
||||
type PluginWorkspaceCommand,
|
||||
type PluginWorkspaceItem,
|
||||
} from './plugin-workspace-model';
|
||||
@@ -402,8 +403,12 @@ export function PluginDetails(props: PluginDetailsProps) {
|
||||
</section>
|
||||
|
||||
<section aria-labelledby="plugin-agents-heading">
|
||||
<h3 id="plugin-agents-heading" className="text-lg font-semibold">伙伴分配</h3>
|
||||
{props.item.source === 'local'
|
||||
<h3 id="plugin-agents-heading" className="text-lg font-semibold">
|
||||
{isProjectWideOfficialPlugin(props.item) ? '生效范围' : '伙伴分配'}
|
||||
</h3>
|
||||
{isProjectWideOfficialPlugin(props.item)
|
||||
? <p className="mt-2 text-pretty text-sm text-muted-foreground">启用当前项目后,插件会自动提供给该项目的主伙伴,无需单独分配;子伙伴不会继承。</p>
|
||||
: props.item.source === 'local'
|
||||
? <p className="mt-2 text-pretty text-sm text-muted-foreground">本机包全局生效,不作为项目 Agent Skill 分配项;符合条件的新建或空闲主伙伴及其 worker 会按既有生命周期加载。</p>
|
||||
: props.item.assignedAgentNames.length
|
||||
? <div className="mt-3 flex flex-wrap gap-2">{props.item.assignedAgentNames.map((name) => <Badge key={name} variant="secondary">{name}</Badge>)}</div>
|
||||
|
||||
@@ -21,7 +21,11 @@ import type {
|
||||
PluginWorkspaceProjection,
|
||||
PluginWorkspaceState,
|
||||
} from './plugin-workspace-model';
|
||||
import { buildPluginWorkspaceProjection, isBundledOfficialPlugin } from './plugin-workspace-model';
|
||||
import {
|
||||
buildPluginWorkspaceProjection,
|
||||
isBundledOfficialPlugin,
|
||||
isProjectWideOfficialPlugin,
|
||||
} from './plugin-workspace-model';
|
||||
import { resolvePluginWorkspaceSearch, serializePluginWorkspaceSearch } from './plugin-workspace-query';
|
||||
|
||||
const DELIVERY_TEXT = {
|
||||
@@ -82,10 +86,15 @@ function cardSource(item: PluginWorkspaceItem): string {
|
||||
|
||||
function agentText(item: PluginWorkspaceItem): string {
|
||||
if (item.source === 'local') return '本机全局生效';
|
||||
if (isProjectWideOfficialPlugin(item)) return '随项目启用';
|
||||
const count = item.assignedAgentIds.length;
|
||||
return count ? `已分配 ${count} 位伙伴` : '尚未分配伙伴';
|
||||
}
|
||||
|
||||
function agentLabel(item: PluginWorkspaceItem): string {
|
||||
return isProjectWideOfficialPlugin(item) ? '生效范围' : '伙伴';
|
||||
}
|
||||
|
||||
function PluginCard({
|
||||
item,
|
||||
grouped = false,
|
||||
@@ -112,7 +121,7 @@ function PluginCard({
|
||||
<dl className="mt-4 space-y-2 text-sm">
|
||||
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">当前项目</dt><dd className="text-right font-medium">{PROJECT_TEXT[item.projectState]}</dd></div>
|
||||
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">本机状态</dt><dd className="text-right font-medium">{item.source === 'local' ? (item.localEnabled ? '本机全局已启用' : '本机全局已停用') : item.official?.installation?.version ? `官方包 ${item.official.installation.version}` : isBundledOfficialPlugin(item) ? '随应用提供' : '未下载官方包'}</dd></div>
|
||||
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">伙伴</dt><dd className="text-right font-medium">{agentText(item)}</dd></div>
|
||||
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">{agentLabel(item)}</dt><dd className="text-right font-medium">{agentText(item)}</dd></div>
|
||||
<div className="flex justify-between gap-4"><dt className="text-muted-foreground">计费</dt><dd className="text-right font-medium">{BILLING_TEXT[item.billing]}</dd></div>
|
||||
</dl>
|
||||
{item.deviceReason ? (
|
||||
@@ -182,7 +191,7 @@ export function PluginsView(props: PluginsViewProps) {
|
||||
<p className="text-sm font-medium text-brand">MakeLore Code</p>
|
||||
<h1 className="mt-1 text-balance text-3xl font-semibold tracking-[-0.03em]">插件</h1>
|
||||
<p className="mt-2 max-w-3xl text-pretty text-sm leading-6 text-muted-foreground">
|
||||
MakeLore 运营发布官方插件;本机 Skill 和 Pi extension 仅通过对话安装。获取、项目启用与伙伴分配彼此独立,本页集中展示账号、设备、当前项目与伙伴状态。
|
||||
MakeLore 运营发布官方插件;本机 Skill 和 Pi extension 仅通过对话安装。获取与项目启用彼此独立;需要定向生效的插件还可分配伙伴。
|
||||
</p>
|
||||
</div>
|
||||
<Button type="button" variant="outline" className="min-h-10" onClick={() => void props.onRefresh()}>
|
||||
|
||||
@@ -9,7 +9,10 @@ import type {
|
||||
MarketplaceUsageBilling,
|
||||
} from '@/lib/plugin-marketplace';
|
||||
import type { DevicePackageIndexV1, DevicePackageRecordV1 } from '../../../shared/device-packages';
|
||||
import { isCodeOwnedOptionalBundledPluginId } from '../../../shared/coding-plugins';
|
||||
import {
|
||||
isCodeOwnedOptionalBundledPluginId,
|
||||
isProjectWideCodingPluginId,
|
||||
} from '../../../shared/coding-plugins';
|
||||
|
||||
export type PluginWorkspaceScope = 'all' | 'project';
|
||||
export type PluginWorkspaceSource = 'all' | 'official' | 'local';
|
||||
@@ -109,6 +112,10 @@ export function isBundledOfficialPlugin(item: PluginWorkspaceItem): boolean {
|
||||
&& (item.delivery === 'system_included' || isCodeOwnedOptionalBundledPluginId(item.pluginId));
|
||||
}
|
||||
|
||||
export function isProjectWideOfficialPlugin(item: PluginWorkspaceItem): boolean {
|
||||
return item.source === 'official' && isProjectWideCodingPluginId(item.pluginId);
|
||||
}
|
||||
|
||||
export type PluginWorkspaceNotice =
|
||||
| { kind: 'scope_fallback'; message: string }
|
||||
| { kind: 'source_unavailable'; source: 'catalog' | 'library' | 'device' | 'project'; message: string }
|
||||
@@ -257,7 +264,7 @@ function officialCommands(
|
||||
} else if (deliveryReady && sourceAvailable && record.project.state !== 'unavailable') {
|
||||
commands.push({ kind: 'enable_project', projectId: input.activeProject.id, pluginId: record.pluginId });
|
||||
}
|
||||
if (deliveryReady && sourceAvailable) {
|
||||
if (deliveryReady && sourceAvailable && !isProjectWideCodingPluginId(record.pluginId)) {
|
||||
commands.push({ kind: 'open_agent_assignment', projectId: input.activeProject.id, pluginId: record.pluginId });
|
||||
}
|
||||
if (record.project.enabled && deliveryReady && sourceAvailable && record.project.settingsSurface) {
|
||||
|
||||
Reference in New Issue
Block a user