fix(plugins): load official hosted plugins from bundled resources
This commit is contained in:
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button';
|
||||
import type { MarketplaceInstallation, MarketplaceLibrarySnapshot } from '@/lib/plugin-marketplace';
|
||||
import { pluginMarketplaceStore, usePluginMarketplaceStore } from '@/stores/plugin-marketplace';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { isCodeOwnedOptionalBundledPluginId } from '../../../shared/coding-plugins';
|
||||
|
||||
function failureText(reason?: string): string | null {
|
||||
if (!reason) return null;
|
||||
@@ -55,7 +56,7 @@ export function MyPluginsView(props: MyPluginsViewProps) {
|
||||
const visibleError = actionErrorText(props.error);
|
||||
return (
|
||||
<main data-testid="my-plugins-page" className="mx-auto w-full max-w-7xl text-foreground">
|
||||
<header className="flex flex-wrap items-start justify-between gap-4"><div><h1 className="text-balance text-3xl font-semibold tracking-[-0.03em]">我的插件</h1><p className="mt-2 max-w-2xl text-pretty text-sm text-muted-foreground">账号插件库与本机下载分开管理。下载不会自动启用到项目,也不会分配给伙伴。</p></div><Button type="button" variant="outline" className="min-h-10" onClick={() => void props.onRefresh()}><RefreshCw className="mr-2 h-4 w-4" />刷新</Button></header>
|
||||
<header className="flex flex-wrap items-start justify-between gap-4"><div><h1 className="text-balance text-3xl font-semibold tracking-[-0.03em]">我的插件</h1><p className="mt-2 max-w-2xl text-pretty text-sm text-muted-foreground">官方插件随 MakeLore 提供;其他插件的账号获取与本机下载分开管理。任何插件都不会自动启用到项目或分配给伙伴。</p></div><Button type="button" variant="outline" className="min-h-10" onClick={() => void props.onRefresh()}><RefreshCw className="mr-2 h-4 w-4" />刷新</Button></header>
|
||||
{props.library?.stale ? <p role="status" className="mt-4 rounded-xl bg-warning/10 p-3 text-pretty text-sm">正在显示上次可信的插件库;服务恢复前,在线计量或托管能力保持不可用。</p> : null}
|
||||
{props.state === 'loading' && !props.library ? <p role="status" className="mt-8 text-sm">正在读取我的插件…</p> : null}
|
||||
{props.state === 'error' && !props.library ? <div role="alert" className="mt-8 rounded-2xl bg-warning/10 p-5"><h2 className="font-semibold">无法读取我的插件</h2><p className="mt-2 text-pretty text-sm">{props.error ?? '请稍后重试。'}</p></div> : null}
|
||||
@@ -65,7 +66,8 @@ export function MyPluginsView(props: MyPluginsViewProps) {
|
||||
{props.library?.items.map((plugin) => {
|
||||
const installed = props.installations[plugin.pluginId];
|
||||
const systemIncluded = plugin.acquisition === 'system_included';
|
||||
const hasDevicePackage = !systemIncluded && Boolean(installed?.version);
|
||||
const bundledDelivery = systemIncluded || isCodeOwnedOptionalBundledPluginId(plugin.pluginId);
|
||||
const hasDevicePackage = !bundledDelivery && Boolean(installed?.version);
|
||||
const removed = plugin.removedAt !== null;
|
||||
const suspended = plugin.runtimeStatus === 'suspended';
|
||||
const retired = plugin.catalogStatus === 'retired';
|
||||
@@ -74,17 +76,18 @@ export function MyPluginsView(props: MyPluginsViewProps) {
|
||||
const updateAvailable = Boolean(hasDevicePackage && channelVersion && installed?.version !== channelVersion);
|
||||
const betaChannelUnavailable = Boolean(hasDevicePackage && installedChannel === 'beta' && !plugin.betaVersion);
|
||||
const busy = Object.keys(props.pending).some((key) => key.endsWith(`:${plugin.pluginId}`));
|
||||
const failure = systemIncluded ? null : failureText(installed?.reason);
|
||||
const failure = bundledDelivery ? null : failureText(installed?.reason);
|
||||
return <article key={plugin.pluginId} className="rounded-2xl bg-background p-5 shadow-soft ring-1 ring-border/70">
|
||||
<div className="flex flex-wrap items-start justify-between gap-4"><div><div className="flex flex-wrap items-center gap-2"><h2 className="text-balance text-xl font-semibold">{plugin.title}</h2>{systemIncluded ? <Badge variant="outline">系统内置</Badge> : removed ? <Badge variant="outline">已移除</Badge> : <Badge variant="secondary">已获取</Badge>}</div><p className="mt-2 text-pretty text-sm text-muted-foreground">{plugin.summary}</p></div><div className="text-right text-sm"><p className="tabular-nums">稳定版 {plugin.stableVersion ?? '不可用'}</p>{plugin.betaVersion ? <p className="mt-1 tabular-nums text-muted-foreground">Beta 版 {plugin.betaVersion}</p> : null}{systemIncluded ? <p className="mt-1 text-muted-foreground">随 MakeLore 提供</p> : hasDevicePackage ? <><p className="mt-1 tabular-nums text-muted-foreground">设备版本 {installed?.version}</p><p className="mt-1 text-muted-foreground">当前频道:{installedChannel === 'beta' ? 'Beta' : '稳定'}</p></> : <p className="mt-1 text-muted-foreground">尚未下载到设备</p>}</div></div>
|
||||
<div className="flex flex-wrap items-start justify-between gap-4"><div><div className="flex flex-wrap items-center gap-2"><h2 className="text-balance text-xl font-semibold">{plugin.title}</h2>{systemIncluded ? <Badge variant="outline">系统内置</Badge> : removed ? <Badge variant="outline">已移除</Badge> : <Badge variant="secondary">已获取</Badge>}</div><p className="mt-2 text-pretty text-sm text-muted-foreground">{plugin.summary}</p></div><div className="text-right text-sm"><p className="tabular-nums">稳定版 {plugin.stableVersion ?? '不可用'}</p>{plugin.betaVersion ? <p className="mt-1 tabular-nums text-muted-foreground">Beta 版 {plugin.betaVersion}</p> : null}{bundledDelivery ? <p className="mt-1 text-muted-foreground">随 MakeLore 提供</p> : hasDevicePackage ? <><p className="mt-1 tabular-nums text-muted-foreground">设备版本 {installed?.version}</p><p className="mt-1 text-muted-foreground">当前频道:{installedChannel === 'beta' ? 'Beta' : '稳定'}</p></> : <p className="mt-1 text-muted-foreground">尚未下载到设备</p>}</div></div>
|
||||
<div className="mt-4 space-y-1 text-pretty text-sm">{suspended ? <p className="text-destructive">运行已暂停</p> : null}{retired ? <p>{removed ? '已退役,移除后不可重新获取' : '已退役;现有账号插件库仍可继续使用受支持版本'}</p> : null}{betaChannelUnavailable ? <p>当前 Beta 频道暂无可用版本;不会静默切回稳定版。</p> : null}{failure ? <p className="text-destructive"><AlertCircle className="mr-1 inline h-4 w-4" />{failure}</p> : null}</div>
|
||||
<div className="mt-5 flex flex-wrap gap-2">
|
||||
{systemIncluded ? null
|
||||
: removed ? <Button type="button" className="min-h-10" disabled={busy || retired || suspended} aria-label={`重新免费获取${plugin.title}`} onClick={() => void props.onReacquire(plugin.pluginId)}>重新免费获取</Button>
|
||||
: bundledDelivery ? null
|
||||
: !hasDevicePackage ? <Button type="button" className="min-h-10" disabled={busy || suspended} aria-label={`下载${plugin.title}`} onClick={() => void props.onInstall(plugin.pluginId)}><Download className="mr-2 h-4 w-4" />下载</Button>
|
||||
: updateAvailable ? <Button type="button" className="min-h-10" disabled={busy || suspended} aria-label={`${installedChannel === 'beta' ? '更新 Beta' : '更新'}${plugin.title}`} onClick={() => void (installedChannel === 'beta' ? props.onInstallBeta(plugin.pluginId) : props.onUpdate(plugin.pluginId))}><RefreshCw className="mr-2 h-4 w-4" />{installedChannel === 'beta' ? '更新 Beta' : '更新'}</Button>
|
||||
: <span className="inline-flex min-h-10 items-center px-2 text-sm font-medium"><PackageCheck className="mr-2 h-4 w-4 text-brand" />设备版本已是最新</span>}
|
||||
{!removed && plugin.acquisition !== 'system_included' && plugin.betaVersion && installedChannel !== 'beta' ? <Button type="button" variant="outline" className="min-h-10" disabled={busy || suspended} aria-label={`安装 Beta${plugin.title}`} onClick={() => void props.onInstallBeta(plugin.pluginId)}>安装 Beta</Button> : null}
|
||||
{!removed && !bundledDelivery && plugin.betaVersion && installedChannel !== 'beta' ? <Button type="button" variant="outline" className="min-h-10" disabled={busy || suspended} aria-label={`安装 Beta${plugin.title}`} onClick={() => void props.onInstallBeta(plugin.pluginId)}>安装 Beta</Button> : null}
|
||||
{!removed && plugin.acquisition !== 'system_included' ? <Button type="button" variant="outline" className="min-h-10" disabled={busy} aria-label={`移除${plugin.title}`} onClick={() => void props.onRemove(plugin.pluginId)}><Trash2 className="mr-2 h-4 w-4" />移除</Button> : null}
|
||||
{hasDevicePackage ? <Button type="button" variant="ghost" className="min-h-10" disabled={busy} aria-label={`删除设备上的${plugin.title}`} onClick={() => void props.onUninstall(plugin.pluginId)}>删除设备包</Button> : null}
|
||||
<Button asChild variant="outline" className="min-h-10"><Link to="/project-plugins">启用到项目</Link></Button>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { cn } from '@/lib/utils';
|
||||
import { codingPluginsStore, useCodingPluginsStore } from '@/stores/coding-plugins';
|
||||
import { pluginMarketplaceStore, usePluginMarketplaceStore } from '@/stores/plugin-marketplace';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { isCodeOwnedOptionalBundledPluginId } from '../../../shared/coding-plugins';
|
||||
import type { MarketplaceInstallation, MarketplaceLibrarySnapshot } from '@/lib/plugin-marketplace';
|
||||
import { useCodingWorkspaceStore } from '@/stores/coding-workspace';
|
||||
import type { DataServiceInstanceState } from '../../../shared/data-service';
|
||||
@@ -93,7 +94,7 @@ export function ProjectPluginsView(props: ViewProps) {
|
||||
{selected.enabled ? <Button type="button" variant="outline" className="min-h-10" disabled={pendingToggle} onClick={() => setDisableCandidate(selected)} aria-label={`禁用${selected.displayName}`}>禁用</Button> : <Button type="button" className="min-h-10" disabled={pendingToggle || selected.state === 'unavailable'} onClick={() => void props.onSetEnabled(selected.id, true)} aria-label={`启用到项目${selected.displayName}`}>启用到项目</Button>}
|
||||
</div>
|
||||
{libraryEntry?.removedAt ? <p role="status" className="mt-4 rounded-xl bg-warning/10 p-3 text-pretty text-sm">账号插件库已移除;项目配置仍保留此插件 ID,重新免费获取后可恢复。</p> : null}
|
||||
{libraryEntry?.acquisitionMode === 'user_acquired' && !libraryEntry.removedAt && !installation ? <p role="status" className="mt-4 rounded-xl bg-warning/10 p-3 text-pretty text-sm">插件已免费获取但尚未下载到本机,当前项目配置会继续保留。</p> : null}
|
||||
{libraryEntry?.acquisitionMode === 'user_acquired' && !libraryEntry.removedAt && !installation && !isCodeOwnedOptionalBundledPluginId(selected.id) ? <p role="status" className="mt-4 rounded-xl bg-warning/10 p-3 text-pretty text-sm">插件已免费获取但尚未下载到本机,当前项目配置会继续保留。</p> : null}
|
||||
<section className="mt-6 grid gap-3 sm:grid-cols-2 lg:grid-cols-4" aria-label="插件概览">
|
||||
<div className="rounded-xl bg-surface-subtle p-3"><p className="text-xs text-muted-foreground">发布者</p><p className="mt-1 font-semibold">MakeLore</p></div>
|
||||
<div className="rounded-xl bg-surface-subtle p-3"><p className="text-xs text-muted-foreground">包版本</p><p className="mt-1 font-semibold">{selected.version}</p></div>
|
||||
|
||||
Reference in New Issue
Block a user