LinPlayer 插件宿主清单(V1 详细草案)
0. 文档状态
本文面向 LinPlayer 宿主开发者,用于统一插件系统的产品边界、协议定义、跨端交付范围和实现顺序。
本文是 目标 V1 规范,不等于当前仓库已经全部实现。阅读本文时应始终区分两件事:
- 当前代码基线已经支持了什么
- 宿主为了正式开放多人协作开发插件,还必须补齐什么
当前 V1 目标平台:
- PC:Windows、macOS
- Mobile:iOS、Android
本文暂不覆盖:
- Linux
- TV
如果未来要扩展到 Linux / TV,应在 V1 完成后另写平台适配附录,而不是在当前版本里混写。
1. 文档目的
宿主文档需要解决四个问题:
- 给宿主开发者一份明确的交付边界,知道插件系统的 V1 到底要做多大
- 给协议维护者一份统一标准,避免代码、插件仓库、文档三套口径不一致
- 给跨端开发者一份平台清单,避免桌面端先做一套、移动端再补一套且行为不一致
- 给后续插件作者一份稳定预期,知道哪些能力是“可依赖的”,哪些还只是计划
2. 术语定义
为了避免讨论时混淆,先固定下面这些术语:
- 宿主:LinPlayer 主应用
- 插件:由独立
manifest.json + script + assets组成的可安装扩展包 - 页面:插件提供的独立页面,由宿主放入“插件中心”或推荐入口
- 插槽:宿主预留给插件注入 UI 的位置
- 运行时:宿主加载并执行插件脚本的环境
- UI Schema:插件返回的 JSON UI 描述,由宿主渲染
- Action:插件返回给宿主执行的动作,如
toast、navigate - 目标端:插件声明支持的平台端,V1 关注
pc和mobile - 市场仓库:托管插件清单、版本、阻断列表和静态展示页面的仓库
- Kill switch:通过
blocked.json对某个插件或某个版本进行紧急禁用
3. 产品目标
V1 要满足的核心目标只有三类:
3.1 插件可以提供独立页面
用户可以在宿主里打开一个插件页面,像打开首页、工具页、专题页一样使用插件功能。
这种页面需要支持:
- 标题
- 页面入口名
- 插件内导航
- 列表、分组、卡片、双栏等信息展示
- 加载、空态、错误态
3.2 插件可以注入卡片和小组件
插件可以在宿主预留位置里插入一块 UI,例如:
- 首页顶部卡片
- 首页底部补充信息
- 详情页动作区按钮
- 详情页底部扩展区
- 播放器右上角工具按钮
3.3 插件可以有自定义数据来源
插件可以自己决定数据来自哪里,例如:
- 某个公开网站
- 某个 JSON API
- 某个聚合服务
但是数据获取必须经过宿主提供的 受控网络能力,不能直接绕过宿主。
4. 明确不做什么
V1 明确不做以下能力:
- 不允许插件直接注入任意 Flutter Widget
- 不允许插件直接注入任意 iOS / Android / macOS / Windows 原生控件
- 不允许插件常驻后台执行任务
- 不允许插件做定时任务、计划任务、后台同步
- 不允许插件直接修改宿主数据库或内部业务状态
- 不允许插件直接接管播放器内核、媒体库、路由系统
- 不允许插件以“完整网页嵌入模式”作为主要协议
- 不允许插件自定义新的宿主 Action 类型并要求宿主立即支持
这意味着:
- V1 的“自定义组件”不是“任意原生组件注入”,而是“用宿主白名单组件拼装出自定义卡片和页面”
- V1 的“自定义数据来源”不是“宿主提供通用爬虫 DSL”,而是“插件通过
ctx.net.request()自己抓取和整理数据” - V1 可以允许受控
webview节点,但它只能作为辅助展示组件,不能替代宿主页面结构、路由和 Action 体系
5. V1 能力分层
为了避免插件系统一开始就失控,V1 能力按下面五层拆开:
5.1 安装与治理层
负责:
- 安装
- 校验
- 启用 / 禁用
- 卸载
- 更新
- block / kill switch
5.2 运行时层
负责:
- 加载插件入口脚本
- 向插件函数传入
ctx - 管理插件调用超时、异常和生命周期
5.3 渲染层
负责:
- 把插件返回的 UI Schema 渲染为宿主 UI
- 控制白名单组件
- 处理插件触发的交互事件
5.4 页面与插槽层
负责:
- 插件独立页面
- 插件插槽注入
- 跨插件页面导航
5.5 市场与运营层
负责:
registry.json- 市场展示页
- 更新检查
blocked.json- 插件来源可信度
6. 当前代码基线
当前仓库已经有一部分插件基础设施:
- [x] 支持通过
manifest.jsonURL 手动安装插件 - [x] 支持按
files[].size + files[].sha256下载和校验资源 - [x] 安装时会读取仓库根
blocked.json - [x] 有 JS 运行时桥接
- [x] 有
ctx.net.request / ctx.storage / ctx.log / ctx.settings - [x] 已支持
pages - [x] 已支持
slots - [x] 桌面端已实现
pages.entry=true顶栏入口
当前仍然明显缺少:
- [ ] 启动时复查已安装插件是否被 block
- [ ] 定期复查已安装插件是否被 block
- [ ] 读取
registry.json作为市场入口 - [ ] 插件更新检查与升级流程
- [ ] 移动端与桌面端统一的 slot 挂点
- [ ]
section / grid / iconButton / chip / badge这批 V1 目标组件 - [ ] 正式的
webview节点规范、导航拦截和跨端一致性 - [ ] 统一且正式的“插件中心”跨端体验
7. 宿主总体架构
宿主插件系统推荐按下面的链路组织:
7.1 市场仓库
市场仓库负责托管:
plugins/<pluginId>/<version>/...registry.jsonblocked.jsonschemas/- 说明文档和静态展示页
7.2 宿主安装器
宿主安装器负责:
- 下载
manifest.json - 校验版本和协议兼容性
- 下载
files[] - 校验文件大小和
sha256 - 写入本地插件目录
- 记录已安装插件列表
7.3 宿主运行时
宿主运行时负责:
- 加载插件入口脚本
- 构造
ctx - 调用
render()和onEvent() - 转换 JS 返回值
- 分发 Action
- 回收运行时资源
7.4 宿主渲染器
宿主渲染器负责:
- 渲染白名单 UI 节点
- 处理节点事件
- 处理加载 / 空态 / 错误态
- 对未知节点优雅降级
7.5 宿主治理层
宿主治理层负责:
- 启用 / 禁用插件
- 启动时 block 检查
- 定期 block 检查
- 更新检查
- 故障隔离和日志
8. 插件生命周期
V1 宿主必须明确实现完整生命周期,否则插件生态会失控。
8.1 发现
插件来源分两类:
- 市场安装:从
registry.json发现插件 - 手动安装:用户粘贴某个版本的
manifest.jsonURL
8.2 安装
安装步骤:
- 下载
manifest.json - 校验
schemaVersion、apiVersion、minHostVersion - 校验目标端是否匹配当前平台
- 读取并校验
blocked.json - 下载
files[] - 校验
size - 校验
sha256 - 写入本地目录
- 更新已安装插件索引
8.3 启用
启用插件不是“立刻常驻运行”,而是:
- 把插件标记为可参与页面和插槽渲染
- 在需要显示时才懒加载运行时
8.4 运行
运行条件:
- 用户打开插件页面
- 宿主页面渲染到某个 slot
宿主不应该在应用启动时一次性加载所有插件脚本。
8.5 交互
插件交互流程:
- 宿主调用
render(ctx, params, state) - 插件返回 UI Schema
- 宿主渲染 UI
- 用户点击或触发事件
- 宿主调用
onEvent(ctx, event, state) - 插件返回
state和actions - 宿主更新状态并执行白名单动作
8.6 更新
V1 宿主必须支持:
- 比较当前已安装版本和市场最新版本
- 用户确认后升级插件
- 升级后保留最新版本目录
- 清理旧版本目录
8.7 禁用
禁用后的效果:
- 插件不再出现在“插件中心”
- 插件页面入口不再显示
- 插槽不再参与渲染
- 插件运行时实例被销毁
8.8 卸载
卸载后的效果:
- 本地插件目录删除
- 已安装索引删除
- 页面和插槽入口全部消失
- 插件私有存储建议一并清理
8.9 Block
当插件或某个版本被加入 blocked.json 时:
- 新安装必须被拒绝
- 已安装插件必须在启动时被重新检查
- 已安装插件应自动禁用,并给用户明确提示
9. 协议治理清单
在正式开放给外部插件作者之前,宿主必须把协议治理做好。
9.1 固定版本
- [ ] 固定
schemaVersion=1 - [ ] 固定
apiVersion=1 - [ ] 冻结 V1 字段集合
9.2 统一字段规则
- [ ]
id使用统一命名规则,建议反向域名格式 - [ ]
version必须是合法 SemVer - [ ]
route必须以/plugin/开头 - [ ]
pages.onEvent在 V1 中固定为必填 - [ ]
slots.onEvent在 V1 中固定为必填 - [ ] 对外 schema、仓库校验脚本、宿主解析器必须保持一致
9.3 兼容性策略
- [ ] V1 期间新增能力只允许通过“向后兼容”的方式添加
- [ ] 删除字段或改变字段语义必须升级
apiVersion - [ ] 宿主升级后不得悄悄破坏已有 V1 插件
10. 运行时契约
宿主运行时必须让插件作者清楚知道“宿主会怎么调我”。
10.1 入口脚本
插件入口脚本由宿主加载并执行。执行后,宿主需要能从全局作用域找到 manifest 声明的 handler。
统一调用方式:
globalThis[handlerName](ctx, ...args)10.2 ctx 最小集合
V1 宿主至少要提供这些字段:
ctx.targetctx.localectx.timeZonectx.hostVersionctx.pluginctx.settingsctx.net.request(req)ctx.storage.get(key)ctx.storage.set(key, value)ctx.storage.remove(key)ctx.log(level, message, extra?)
10.3 超时与异常
宿主必须:
- [ ] 对插件函数调用设置超时
- [ ] 捕获脚本异常
- [ ] 将异常转化为宿主可展示的错误信息
- [ ] 防止单个插件异常导致宿主页面整体崩溃
10.4 生命周期回收
宿主必须:
- [ ] 页面销毁时回收运行时
- [ ] 插槽组件销毁时回收运行时
- [ ] 禁用 / 卸载时回收运行时
10.5 脚本运行时与 webview 隔离
宿主必须明确区分两类环境:
- 插件入口脚本运行在宿主控制的脚本运行时里
webview节点加载的是受控网页内容,不属于插件脚本运行时
宿主必须保证:
- [ ]
webview页面不能直接访问ctx - [ ]
webview页面不能直接访问宿主 JS bridge - [ ] 插件不能依赖网页内脚本直接调用宿主私有 API
- [ ] 插件如果要改变
webview内容,只能通过重新返回 schema 让宿主重渲染
11. 数据来源策略
这是 V1 最容易被做乱的一块,必须明确。
11.1 V1 不做声明式数据源 DSL
V1 不额外设计独立的 dataSources DSL 让宿主去执行。
原因:
- 宿主实现复杂度高
- 跨端一致性成本高
- 插件作者真实需求主要是“拉数据并展示”,不是“把抓数逻辑移交给宿主”
11.2 V1 的数据来源实现方式
V1 统一采用:
- 插件自己通过
ctx.net.request()拉取数据 - 插件自己完成解析、标准化和拼装 UI Schema
- 宿主只负责网络权限、日志、存储、渲染和动作执行
11.3 宿主必须提供的支撑
- [ ] 网络能力
- [ ] 私有缓存存储
- [ ] 超时和并发控制
- [ ] 域名白名单
- [ ] 错误日志
11.4 推荐的数据流
推荐插件作者按下面顺序组织代码:
- 获取远程数据
- 解析原始数据
- 标准化为页面用的数据模型
- 生成 UI Schema
- 在失败时返回
error或empty
宿主文档必须把这条推荐路径写清楚,避免插件作者一上来就把远程 HTML 原样塞给宿主。
12. UI Schema 宿主责任
宿主必须对外公布 V1 白名单节点,并且真正实现这些节点。
12.1 V1 白名单节点
- [ ]
page - [ ]
section - [ ]
row - [ ]
column - [ ]
list - [ ]
grid - [ ]
card - [ ]
divider - [ ]
spacer - [ ]
text - [ ]
markdown - [ ]
image - [ ]
webview - [ ]
button - [ ]
iconButton - [ ]
chip - [ ]
badge - [ ]
loading - [ ]
empty - [ ]
error
12.2 宿主必须保证的行为
- [ ] 所有节点都由宿主渲染
- [ ] 所有可交互节点都只能发出宿主认可的事件
- [ ] 未识别节点不会导致崩溃
- [ ] 未识别节点会给出明确降级提示
- [ ] 图标通过字符串白名单映射,不接受原生对象
12.3 宿主推荐的组件语义
为了让插件作者能做出“像首页一样”的页面,宿主需要给出语义而不是只给 row/column。
建议:
page:页面根容器section:带标题的区块grid:卡片网格card:一块信息或操作卡片badge:信息标签chip:轻量操作项iconButton:图标按钮,适合工具栏和播放器区
12.4 webview 组件规范
webview 是 V1 允许的受控补充节点,用来承载少量必须保留网页原始交互的内容。 它不是插件主要协议,也不是“把整站塞进宿主”的兜底方案。
V1 只保证 webview 在插件 page 场景可用。 对于 slot 场景,宿主可以直接拒绝渲染,或只允许固定高度的小块内容。
最小 schema 建议:
{
"type": "webview",
"props": {
"src": "https://example.com/embed/help",
"height": 420,
"title": "帮助页",
"allowExternalNavigation": false,
"showProgress": true
}
}字段规则:
src必填,必须是绝对http/httpsURLheight必填,单位按宿主逻辑像素解释;宿主可以设置最小值和最大值title可选,用于标题补充、无障碍和错误提示allowExternalNavigation可选,默认falseshowProgress可选,默认true
宿主必须保证:
- [ ] 顶层初始 URL 和后续顶层导航都受域名白名单控制;V1 统一复用
permissions.network.domains - [ ] 默认禁用弹窗、新窗口、文件下载和非
http/httpsscheme - [ ] 网页环境不得注入
ctx、宿主 JS bridge 或插件私有状态 - [ ]
webview加载失败必须显示错误态,而不是白屏 - [ ] 页面关闭、插件禁用、插件卸载时必须销毁对应
webview实例 - [ ] 插件不能通过
webview劫持宿主返回键、返回手势或路由栈
宿主推荐策略:
- 对超出白名单的跳转,降级为
openUrl或给出明确提示 - 在
slot中默认禁用webview,确有需要时只允许固定高度且不可抢占主滚动 - 如平台支持,按插件 ID 做 cookie / storage 隔离;做不到时至少不得复用宿主认证态
明确不允许:
- 插件把首页或唯一页面做成整页远端 SPA
- 插件依赖网页内脚本直接调用宿主私有 API
- 插件把大面积可滚动
webview注入核心 slot
13. Action 白名单
V1 Action 必须严格受控。
13.1 宿主必须支持
- [ ]
toast - [ ]
navigate - [ ]
openUrl
13.2 宿主必须保证
- [ ] 未知 Action 直接忽略
- [ ] Action 执行失败不导致插件崩溃
- [ ]
navigate只允许应用内路由 - [ ]
openUrl只允许系统允许的外链打开方式
14. 页面能力清单
插件页面是你这次需求里的核心,因此宿主必须把页面能力做完整。
14.1 插件中心
宿主必须提供统一“插件中心”,至少包含:
- [ ] 页面列表
- [ ] 名称
- [ ] 描述
- [ ] 图标
- [ ] 当前版本
- [ ] 启用状态
- [ ] 打开入口
14.2 页面排序
排序规则建议固定:
order升序title升序plugin.id + page.id作为最终稳定排序键
14.3 推荐入口
对于 pages.entry=true:
- Windows / macOS:可以在首页顶栏放少量推荐入口
- iOS / Android:可以在插件中心中优先显示,也可以额外放一个首页入口
14.4 页面形态
宿主必须支持这些页面形态:
- 单栏工具页
- 多 section 信息页
- 双栏专题页
- 卡片网格页
- “网页内容结构化展示”页面
补充约束:
webview只能作为上述页面中的辅助块,不能替代页面主结构
14.5 页面参数
宿主打开页面时,可以传:
page来源标识- 当前上下文参数
- 插件内跳转参数
宿主必须保证:
- 参数为空时插件仍能正常打开
- 参数格式不符合预期时插件能收到空对象,而不是崩溃
15. 插槽能力清单
V1 统一 slot 集合如下。
15.1 home.feed.beforeSections
定位:
- 首页主内容区之前
用途:
- 插件入口卡片
- 活动卡片
- 推荐卡片
15.2 home.feed.afterSections
定位:
- 首页主内容区之后
用途:
- 补充说明
- 外部内容流
- 底部统计或专题入口
15.3 detail.hero.actions
定位:
- 详情页主操作区附近
用途:
- 增加按钮
- 增加快捷操作
15.4 detail.sections.bottom
定位:
- 详情页下半部分扩展区
用途:
- 附加信息卡片
- 第三方数据补充
- 外链导航
15.5 player.appbar.trailing
定位:
- 播放器右侧工具区
用途:
- 小型图标按钮
- 状态提示
- 快捷入口
15.6 插槽治理要求
- [ ] 同一
slotId多贡献按priority降序 - [ ] 每个
slotId限制最大渲染数,建议不超过 6 - [ ] 插槽崩溃不影响宿主主页面
- [ ] 插槽过高时允许宿主裁剪或限制
- [ ] 插槽交互不得劫持系统返回键或宿主关键手势
16. 市场与版本治理
如果没有市场治理,插件生态很快会不可维护。
16.1 registry.json
V1 市场必须提供 registry.json,至少用于:
- 展示插件列表
- 展示最新版本
- 展示支持平台
- 给宿主做更新检查
16.2 blocked.json
blocked.json 必须承担:
- 阻止新用户安装被禁用插件
- 阻止用户继续使用已知危险版本
16.3 安装链接规范
建议强制要求:
- 安装链接指向某个版本的
manifest.json - 推荐使用 tag 或 commit SHA
- 避免使用会变化的
main分支 URL 作为长期安装链接
16.4 手动安装保留
即使市场能力上线,也应该保留“手动粘贴 manifest URL 安装”,因为:
- 便于开发调试
- 便于灰度测试
- 便于私有插件验证
17. 安全与审计
17.1 网络权限
宿主必须做到:
- [ ]
permissions.network.enabled默认关闭 - [ ]
permissions.network.domains默认空数组 - [ ] 只允许
http/https - [ ] 控制超时
- [ ] 控制并发
- [ ] 控制最大响应体大小
17.2 资源隔离
宿主必须做到:
- [ ] 单个插件失败不拖垮宿主页面
- [ ] 单个插件超时不拖垮宿主渲染线程
- [ ] 插件日志可以按插件 ID 定位
17.3 外链策略
宿主必须明确:
- [ ] 哪些 URL 允许
openUrl - [ ] 是否提示用户离开应用
- [ ] 是否屏蔽危险 scheme
17.4 webview 安全边界
宿主必须明确:
- [ ]
webview顶层导航白名单与permissions.network.domains对齐 - [ ] 禁止
window.open/ popup / 自定义 scheme 自动拉起 - [ ] 不向网页注入宿主 bridge、token 或内部认证态
- [ ] 白名单拦截、加载失败和证书异常都有明确可见提示
18. 跨端交付清单
18.1 通用清单
- [ ] 协议一致
- [ ] 页面和插槽行为一致
- [ ] Action 一致
- [ ] 安装和更新流程一致
- [ ] 错误处理一致
- [ ]
webview的导航拦截、错误态和销毁时机一致
18.2 Windows / macOS
- [ ] 首页顶栏 entry 入口
- [ ] 双栏 / 网格布局
- [ ] 鼠标点击与滚轮体验
- [ ] 窗口 resize 稳定
- [ ]
iconButton可在工具栏中正常显示
18.3 iOS / Android
- [ ] 单栏优先布局
- [ ] SafeArea 合规
- [ ] 点击区域满足触屏尺寸要求
- [ ] 系统返回与插件路由栈一致
- [ ] 插槽不遮挡主操作区和底部手势区
19. 测试矩阵
V1 开放前,宿主侧至少要有以下测试:
19.1 安装测试
- [ ] URL 非法
- [ ] manifest 缺字段
- [ ] 版本不兼容
- [ ] sha256 不匹配
- [ ] 文件缺失
- [ ] 被 block
19.2 运行时测试
- [ ]
render()正常返回 - [ ]
render()抛异常 - [ ]
onEvent()抛异常 - [ ] 超时
- [ ] 空 schema
- [ ] 未知节点
- [ ]
webview不能访问宿主脚本运行时
19.3 交互测试
- [ ]
toast - [ ]
navigate - [ ]
openUrl - [ ] 页面打开和关闭
- [ ] 插槽交互
- [ ]
webview白名单内导航 - [ ]
webview外域跳转拦截 - [ ]
webview销毁后不残留页面实例
19.4 数据测试
- [ ] 网络超时
- [ ] 白名单域名限制
- [ ] JSON 解析错误
- [ ] 空数据
- [ ] 缓存命中与缓存失效
19.5 平台测试
- [ ] Windows
- [ ] macOS
- [ ] iOS
- [ ] Android
20. 推荐的宿主实施顺序
如果要务实推进,而不是把范围一次性摊大,建议按下面顺序做:
第一阶段:协议冻结
- 固定 manifest 字段
- 固定白名单节点
- 固定 slot 集合
- 固定 Action 集合
第二阶段:补 UI 能力
- 实现
section - 实现
grid - 实现
iconButton - 实现
chip - 实现
badge - 实现受控
webview
第三阶段:补移动端挂点
- 把 5 个核心 slot 在 iOS / Android 上挂齐
- 把插件中心做成正式入口
第四阶段:补治理能力
registry.json- 更新检查
- 启动时 block 检查
- 定期 block 检查
第五阶段:样例验收
- 至少 2 到 3 个插件跨端验证通过
- 至少 1 个首页型页面插件
- 至少 1 个插槽型工具插件
21. 当前代码入口
宿主侧主要实现入口如下:
lib/services/plugins/plugin_manager.dartlib/services/plugins/plugin_runtime_v1.dartlib/plugins/plugins_page.dartlib/plugins/plugin_page_host_page.dartlib/plugins/plugin_slot_area.dartlib/plugins/plugin_schema_renderer.dartlib/desktop_ui/pages/desktop_home_page.dart
22. V1 完成标准
只有当下面这些条件同时满足,才适合正式开放给多人共同创建插件:
- [ ] 协议和 schema 已冻结
- [ ] Windows、macOS、iOS、Android 都能稳定打开插件页面
- [ ] 5 个核心 slot 已跨端挂齐
- [ ] 白名单 UI 节点已全部实现
- [ ] 市场安装、手动安装、更新、block 流程都跑通
- [ ] 至少有一组跨端样例插件通过验收
- [ ] 对外插件作者文档已经与宿主实现完全一致