# PluginService
管理插件的完整生命周期:加载、卸载、查询元数据、访问跨插件资源。
import { useService, useServiceState } from '@autumnbox/sdk/hooks';
import { PluginService } from '@autumnbox/sdk/hooks';
# 查询插件列表
const pluginService = useService(PluginService);
// 获取所有已加载插件的元数据(响应式)
const plugins = useServiceState(pluginService.pluginsState);
每个插件元数据包含:
interface PluginMetadata {
pluginPackageName: string; // 唯一标识
name: string; // 显示名称(已翻译)
description: string; // 描述(已翻译)
author: string; // 作者名(已翻译)
version: string; // 版本号
license?: string; // 许可证
repository?: string; // 源码仓库 URL
}
# 查询单个插件
const meta = pluginService.getPluginMetadata('autumnbox.main-plugin');
if (meta) {
console.log(`${meta.name} v${meta.version} by ${meta.author}`);
}
# 检查是否内置
const isBuiltin = pluginService.isBuiltin('autumnbox.main-plugin');
// true → 不可卸载的内置插件
# 访问跨插件资源
PluginService 可以读取任何已加载插件的打包资源:
// 获取原始 Blob
const blob = pluginService.getPluginResource(
'autumnbox.plugin-store', 'icon.png'
);
// 获取文本
const html = await pluginService.getPluginResourceAsText(
'autumnbox.plugin-store', 'templates/card.html'
);
// 获取 Data URI(自动推断 MIME,适合 img src)
const dataUri = await pluginService.getPluginResourceAsSource(
'autumnbox.plugin-store', 'icon.png'
);
// 列出目录内容
const files = pluginService.listResource(
'autumnbox.plugin-store', 'templates'
);
// → ['card.html', 'detail.html']
Data URI 与 MIME
getPluginResourceAsSource 会根据扩展名自动设置正确的 MIME 类型。这对 SVG 至关重要——浏览器拒绝渲染没有 image/svg+xml MIME 的 SVG data URI。
# 安装与卸载
# 动态安装插件
// 从 File 对象安装(如用户通过 input[type=file] 选择的 .atmb)
const fileInput = document.querySelector('input[type=file]') as HTMLInputElement;
const file = fileInput.files?.[0];
if (file) {
const pluginName = await pluginService.loadPlugin(file, {
persist: true, // 持久化到 OPFS,刷新后保留
uninstallable: true, // 允许卸载
});
console.log(`已安装: ${pluginName}`);
}
# 卸载插件
// 立即卸载
const success = await pluginService.uninstallPlugin('autumnbox.old-plugin');
// 或标记为"下次重载时移除"
await pluginService.addToBeRemoved('autumnbox.old-plugin');
# 更新插件
// 标记待更新(新 .atmb 文件在下次重载时替换旧版)
await pluginService.readyForUpdate('autumnbox.my-plugin', newAtmbFile);
// 触发深度重载
pluginService.deepReload();
# 打开插件的 App
// 打开某个插件注册的所有 App(类似"展开插件"功能)
pluginService.openAppsOf('autumnbox.main-plugin');
// 取消打开
pluginService.dismissOpenApps();
# 待处理操作状态
// 是否有待卸载的插件
const pendingRemovals = useServiceState(pluginService.pendingRemovalsState);
// → ['autumnbox.old-plugin']
// 是否有待更新的插件
const pendingUpdates = useServiceState(pluginService.pendingUpdatesState);
// 是否有任何待处理操作
if (pluginService.hasPendingOperations) {
// 提示用户需要重载
}
# 实战示例:插件列表页
import React from 'react';
import { List, Avatar, Tag, Button, Typography } from 'antd';
import { useService, useServiceState } from '@autumnbox/sdk/hooks';
import { PluginService } from '@autumnbox/sdk/hooks';
const { Text } = Typography;
const PluginList: React.FC = () => {
const pluginService = useService(PluginService);
const plugins = useServiceState(pluginService.pluginsState);
return (
<List
dataSource={plugins}
renderItem={(plugin) => (
<List.Item
actions={[
!pluginService.isBuiltin(plugin.pluginPackageName) && (
<Button
danger
size="small"
onClick={() => pluginService.uninstallPlugin(plugin.pluginPackageName)}
>
卸载
</Button>
),
].filter(Boolean)}
>
<List.Item.Meta
avatar={<PluginIcon pluginPackageName={plugin.pluginPackageName} />}
title={
<>
{plugin.name}
<Tag style={{ marginLeft: 8 }}>v{plugin.version}</Tag>
</>
}
description={plugin.description}
/>
</List.Item>
)}
/>
);
};
const PluginIcon: React.FC<{ pluginPackageName: string }> = ({ pluginPackageName }) => {
const pluginService = useService(PluginService);
const [src, setSrc] = React.useState<string>();
React.useEffect(() => {
pluginService.getPluginResourceAsSource(pluginPackageName, 'icon.png')
.then((uri) => { if (uri) setSrc(uri); });
}, [pluginPackageName]);
return <Avatar src={src} shape="square" />;
};
# 下一步
- 资源系统 — 打包资源的放置和使用方式
- AppService — App 注册表
- 构建与发布 — 构建和发布流程