组件包协议
包格式为schemaVersion=1、hostApiMajor=1,当前作者SDK为0.1.2,更新规则见作者 SDK 版本管理。公开协议和内部管理状态均以 v1 为基线,兼容与迁移遵循格式版本规则(宿主文档:../development.md#格式版本兼容与迁移);存档正文格式由作者自行定义。能力声明及可拒绝授权见宿主接口。
运行兼容性
组件 version 只用于作品更新排序;SDK 发行版本只用于开发与诊断,不要求组件与宿主 SDK 精确匹配。schemaVersion 标识清单结构,hostApiMajor 标识接口及行为兼容代际,分别管理。同代际保持已发布契约兼容;跨代际仅支持明确实现并验证的范围。当前清单结构只支持 schemaVersion=1,运行接口代际只支持 hostApiMajor=1,不根据应用版本猜测支持范围。
清单可声明 requiredFeatures 字符串数组,缺省为空;最多256项,每项1–128个ASCII字母、数字、点、下划线或连字符,不允许重复。值精确匹配宿主握手 features 目录。只声明没有就无法运行的接口或特性;可选功能通过 features 探测并降级。未知的合法标识视为缺少必需功能,拒绝运行;类型、重复或格式错误属于无效清单。功能支持不等于授权,Capability 拒绝不构成版本不兼容,也不阻止启用。新增接口时为其登记稳定特性标识;改变既有接口或特性含义的破坏性变更必须提升接口代际,不能只修改 SDK 版本。
例如 "hostApiMajor": 1, "requiredFeatures": ["saves.load", "saves.commit"] 表示依赖该代际和两项必需功能。作者工具与安装共用校验;旧宿主不认识新增清单字段时会拒绝,不承诺旧宿主读取新清单。
Services 区分结构校验与运行兼容性。新导入、工坊下载、开发重载及候选版本先验证结构与当前支持;不兼容报 PROTOCOL_UNSUPPORTED,列出所需接口代际或缺少的功能。首次安装失败不创建安装;工坊保留订阅及失败原因。更新失败保留已有激活版本、数据和授权,不自动降级,不把下载成功当作安装成功。候选激活、布局应用/恢复及最终提交重新检查兼容性。
已安装且结构可读取的组件即使运行不兼容,仍保留安装、配置、存档、KV、授权、绑定和启用意图;管理界面按不兼容状态(宿主文档:../modules/ui.md)显示用户提示与标记,不创建运行窗口或执行脚本,不影响其他兼容组件恢复。不兼容时管理界面的启用开关置灰,仍可移除;可信停用(包括全部停用)仍取消启用意图。宿主重启或安装更新后重新计算兼容性,恢复兼容时按保留的启用意图自动恢复,期间主动停用者保持停用,断屏者等待回接。兼容状态是当前宿主的派生结果,不持久化为新的启用开关。未知清单结构及损坏数据仍按格式版本规则保留原文件并报错,不能假装已理解未知结构。
布局中的任一依赖不兼容时禁止整屏应用或恢复,预览列出组件及原因,保持原布局。包回退不回退用户存档、KV或权限,当前数据与旧组件业务格式的兼容性由作者负责。
内容目录
一个组件包是一个目录,必须含 manifest.json 与entry指定的入口页面,可包含preview指定的预览及本地静态资源。作者可以使用任意前端构建工具,但交付的是构建后的网页;终端用户不安装 Node.js,也不运行作者的构建或安装脚本。
宿主每次从当前manifest读取entry和preview,缺省分别为index.html和preview.png;entry必须存在,未声明preview且默认文件不存在时使用占位图,显式声明的preview必须存在。两者允许安全的包内子目录路径,入口为HTML、预览为PNG,不能使用URL、绝对路径、路径穿越或链接。JavaScript与框架随包提供,禁止远程JavaScript;Wasm沿用网页资源规则,不加载原生DLL/EXE。
SDK由宿主统一提供,组件包不携带widget-host.js、widget-host.d.ts和protocol.json,入口无需加载SDK脚本;同名文件(不区分大小写)拒绝打包。类型声明只用于包外开发环境,详见开发组件。
本地导入选择目录,宿主校验后复制内容;后续源目录变化不自动影响运行版本。普通导入遇到已安装widgetId时拒绝;来源去重见安装身份(宿主文档:../architecture.md#身份与数据归属)。工坊上传使用内容目录。
location能力需要清单声明和安装授权,拒绝不阻止组件启用;调用边界以宿主接口为准。
内置组件是随应用提供的本地组件包,包含 widgets/weather、widgets/calendar、widgets/todo 和 widgets/notes。仓库examples中的功能验证组件不作为内置组件,不复制到应用输出或本地导出。应用首次发现该包时后台走同一复制、校验与安装流程,成功后持久记录已处理身份;已移除组件不自动装回,可显式重新导入。后续启动检查仍安装的四款内置组件,仅在随附版本高于当前版本时校验并暂存候选;管理宿主沿原有窗口准备、版本预检和提交事务自动激活,保留存档、KV、兼容配置、已有授权、位置、尺寸及启用/显隐偏好,新增权限不自动授予。同版本不替换,较旧随附包不降级;校验或运行准备失败保留当前可用版本,下次启动可重试。运行资源仍来自数据目录中的不可变安装版本。
manifest
window及其size、surface均可省略。size声明初始与最小尺寸,layer声明初始层级,surface声明透明、圆角与输入;所有默认值由宿主同一个解析器提供,不写回作者清单。
| 字段 | 默认值与规则 |
|---|---|
| size.width / height | 320 / 200 DIP,有限正数,最大1000000 |
| size.minWidth / minHeight | 1 / 1 DIP,有限正数,不大于初始尺寸 |
| layer | bottom;可为top,只影响首次实例 |
| surface.transparent | false;透明实例保留作者自定义轮廓 |
| surface.cornerRadius | 不透明默认8 DIP,透明默认0;有限0–64 DIP;透明只允许0 |
| surface.inputMode | rect;透明时可为regions或none,不透明只允许rect |
组件背景由作者在网页内使用 HTML/CSS 绘制,可通过普通配置字段选择样式并响应 config.changed。
圆角画面由宿主合成层抗锯齿裁剪,输入以同一圆角几何按像素中心判定;半径随显示缩放变化,实际不超过窗口短边的一半。0表示直角,透明实例不强制裁剪。regions在导航后从空集合开始,none为纯展示。用户编辑位置及网页拖动、自主移动沿用宿主接口。
{
"schemaVersion": 1,
"kind": "widget",
"widgetId": "f1846617-c096-4c41-b6d1-4622f9b12ab1",
"version": "1.0.0",
"hostApiMajor": 1,
"allowMultipleInstances": false,
"name": "系统概览",
"description": "展示系统资源,采样不可用时显示说明。",
"author": "Example Author",
"tags": ["system"],
"entry": "index.html",
"preview": "preview.png",
"window": {
"size": {"width": 320, "height": 200, "minWidth": 200, "minHeight": 120}
},
"capabilities": [],
"configuration": [
{
"key": "title",
"label": "标题",
"type": "string",
"default": "系统概览",
"maxLength": 80,
"shareable": true
},
{
"key": "refreshSeconds",
"label": "刷新间隔",
"type": "integer",
"default": 5,
"minimum": 1,
"maximum": 60,
"shareable": true
}
]
}| 字段 | 规则 |
|---|---|
| schemaVersion / kind | 必须为 1 / widget;不支持的 schema 拒绝安装,不猜测解释 |
| widgetId | UUID,作品身份;已关联同一来源后不可更换 |
| version | 首版采用三个非负整数 major.minor.patch,不支持预发布后缀;按数字段比较 |
| hostApiMajor | 当前为 1;兼容代际独立管理,见格式版本规则(宿主文档:../development.md#格式版本兼容与迁移) |
| allowMultipleInstances | 可选布尔值,默认false;false时按安装在所有显示器最多一个启用意图,隐藏或断屏仍占用;其他屏启用按钮禁用,服务、布局和版本激活同样检查 |
| name / description / author | UTF-8 文本,name 与 author 非空;只作显示,不作为身份和路径 |
| tags | 可选字符串数组,默认空,支持多选;最多64项,每项1–64个字符。标准英文值及中英文显示名由SDK的protocol.json.contentTags统一维护;重复值合并,未知值保留在作者清单但不展示、不用于工坊上传 |
| i18n | 可选,安全包内JSON路径,使用上述包内多语言规则 |
| entry | 可选,默认 index.html;安全包内HTML相对路径 |
| preview | 可选,默认 preview.png;安全包内PNG相对路径,缺省文件不存在时使用占位图 |
| window | 可选,尺寸、层级及呈现规则见上述字段表 |
| capabilities | 组件声明的能力名称数组;名称唯一,允许空 |
| configuration | 字段数组;key 唯一,遵守配置协议;允许空 |
schema v1 顶层未知字段、未知能力或未知字段类型返回校验错误,避免拼写错误被静默吞掉。发布前扩展同步当前协议、宿主、SDK 与示例;schemaVersion 和消息 protocolVersion 均为 1。窗口初始位置由宿主选择可见区域,manifest 不指定显示器或绝对桌面坐标。窗口默认不透明,透明与交互方式须明确声明;页面样式不能自行改变宿主层级、穿透或屏幕归属。
窗口尺寸由作者决定,默认与最小尺寸来自 window 声明,作者可使用 window.setSize 动态请求自己的实例尺寸。网页不能借尺寸接口改变位置或其他实例。
不允许多实例的组件在版本激活时若已有多个启用实例,激活失败并保留当前版本;用户先停用额外实例后重试。限制按安装身份执行,不把不同来源的安装合并。
组件标签采用单层大类:information(资讯)、productivity(效率)、system(系统)、files(文件)、media(媒体)、decoration(装饰)、entertainment(娱乐,包含桌宠与游戏)、education(教育)、lifestyle(生活)、development(开发)。小写英文标识精确匹配,清单 tags 和工坊内容标签使用该标识;sdk/protocol.json.contentTags 中的 name 保存标识,enUS、zhCN 分别保存英文和简体中文显示名称;应用内按宿主语言显示,呈现位置见管理界面(宿主文档:../modules/ui.md)。SDK 0.1.1 的清单须将原首字母大写标签改为小写;原大写值按未知标签处理,不显示或上传。标签不授予能力,不代替版本或权限声明。Widget和Layout由工坊内容类型自动生成,不属于作者标签。
包内多语言
manifest可选i18n指定包内JSON相对路径;声明后name、description及配置的label、description、group和枚举选项label统一使用完整的{key}引用。未声明i18n的组件继续使用纯文本,不增加旧格式迁移。author、配置key/value/default、身份和用户输入不翻译。
翻译文件格式为{"defaultLanguage":"zh-CN","translations":{"zh-CN":{"name":"天气"},"en-US":{"name":"Weather"}}}。默认语言必须存在;语言标签使用连字符分段的ASCII字母数字,最长35字符,最多32种语言;每种最多2048个键,键为字母开头的英文标识符(允许数字、下划线、点和连字符),最长80字符,值为纯字符串,最长4096个UTF-16单元。整个文件最多128KiB。清单引用必须在默认语言有定义;名称及配置标签/分组翻译不能为空,配置说明继续遵守1024单元限制。
使用宿主实际语言精确查找,缺失键逐项回退到默认语言;动态页面仍缺失的键显示键名并产生一次有界开发诊断。包扫描拒绝缺失文件、无效UTF-8/JSON、错误类型、超限、非法引用及包外/重解析路径,翻译文件参与包哈希;复制校验前后核对内容一致。安装状态保留已校验字典,各视图不得在UI线程另读文件。
宿主管理界面从同一解析器取得组件名、简介、配置及选项文案,用于卡片、详情、搜索和可访问名称;不写回清单或配置。语言变化重新呈现文案,不重启组件,不丢失配置草稿。布局中的组件名快照使用宿主当前语言,仅作为显示提示。
页面使用data-i18n="title"绑定叶元素纯文本,data-i18n-title、data-i18n-aria-label、data-i18n-placeholder绑定指定属性;不扫描普通花括号、不写innerHTML、不替换输入值。每文档最多处理2048个绑定元素,缺失键诊断最多64个。SDK自动绑定初始及后续新增元素,语言变化在作者事件回调前刷新绑定;离开断开观察器。动态内容通过widgetHost.i18n.t(key)取词,widgetHost.i18n.language读取实际语言。页面字典通过握手提供,无额外网络请求或Capability;自动更新事件仍受当前文档会话、暂停及关闭边界约束。
校验与信任
校验 JSON 编码与大小、必填字段、版本、重复键、配置默认值、资源存在与资源尺寸。清点整个目录,拒绝路径穿越、绝对路径、符号链接/reparse point、包外资源及不支持的执行载荷;包内 JavaScript 与 WebAssembly 属于允许的网页资源,不因此获得系统权限。允许 .d.ts 作者类型声明作为静态辅助文件,宿主不编译或执行 TypeScript;其他 .ts 文件仍不支持,作者须先构建成 JavaScript。
首版产品限额:manifest 最大 256 KiB、预览PNG最大 1 MiB 且最长边不超过 4096 像素、单资源最大 64 MiB、整个包最大 256 MiB、文件数最多 10000。导入和下载校验执行相同限制,发布时还必须满足当前 Steam 要求。限额是项目默认值,不冒充 Steam 官方限额;修改须同步本文、实现和测试。
计算内容哈希用于完整性与版本去重;哈希不是发布者可信证明。名称、描述、预览和作者文本均是不可信显示内容,不作为命令或 HTML 执行。
工坊包须来自预期 App ID、条目 ID 及成功下载结果,安装键由可信来源上下文生成,不能信任 manifest 自报的工坊身份。本地包和工坊包执行相同权限规则。
开发来源由宿主登记,manifest不能自行指定安装来源。开发安装使用独立身份、源码绑定及内容修订,例外仅限开发组件;下面版本号规则继续适用于正式本地与工坊安装。
版本激活
订阅、下载、校验、安装和运行是不同阶段。校验失败保留当前可用版本。成功的新版本先存为候选,重启应用或主动重载时切换,同一安装在所有显示器上保持一个激活版本。
针对已有安装的更新提交中,同版本同内容视为无变化;同版本不同内容拒绝,要求作者递增版本。版本回退只通过管理界面的明确恢复操作,不把旧下载自动覆盖为最新。激活失败则恢复原版本与原配置;已撤销权限、卸载记录始终优先,不能回滚授权。
新增能力默认为未授权,不阻止新版运行;删掉的能力移除授权和相关绑定,未来重新声明仍须重新授予。旧版本保留仅服务于激活回滚,最多保存一个上一可用版本;取消订阅时全部删除。
目录读取和绑定目标打开属于基础接口,更新或回退版本时不因Capability声明变化清除directory、launchTarget或secret绑定。移除directory.write只撤回写入许可,既有目录仍可读取;绑定清除仍由显式用户操作和资源有效性规则处理。
准备只改候选,取消或提交失败保留当前绑定。成功激活清除全部实例、上一版本配置备份及恢复快照的资源引用,不改同值普通文本。发生能力/绑定清除的版本提交不保留含旧引用的元数据回退代;包的上一版本仍保留,恢复后须重新授权、选择资源。
发布
发布组件必须先通过上述本地校验和预览。发布包只包含作者内容,不含安装记录、用户配置、profile、绑定或秘密。工坊流程见 Steam 与创意工坊。
Worker 资源约定
Dedicated Worker 入口及 importScripts/module 导入均随包提供 .js/.mjs,使用当前实例包 origin。禁止远程、blob/data 脚本、Shared/Service Worker 与嵌套 Worker;完整执行策略见宿主接口。作者必须终止不用的任务并在主文档失效时丢弃结果,不使用 Worker 绕过实例身份与权限。
作者预检与统一资源规则
protocol.json.packageRules 是包资源扩展名、实际 MIME、类别和文件/整包/条目/路径/深度/预览预算的唯一机器可读维护来源;导入、开发源码指纹、资源响应和作者工具共用它。JS / MJS 仍是仅有的包内 JavaScript 文件类型,声明文件不执行;未知资源或本机执行载荷拒绝,直接联网与 Capability 规则保持各自既定语义。
条目额度同时计算目录和文件,路径检查包含相对 UTF-16 长度、层级深度、控制字符与大小写碰撞;拒绝链接,复制和读取继续使用根范围检查。格式不符直接拒绝。有效包的内容标识维持既有路径、固定长度摘要及换行编码,不因检查工具改写;安装身份、候选版本、删除标记和提交仍由 Services 独占。
作者原生预检只读扫描,不创建安装、运行时或用户数据;与实际导入共用完整包级校验。实际安装仍核对当前安装身份和版本,不把只读报告当作安装承诺。工具可额外诊断有限的静态资源引用,不能替代实际运行或证明全部动态路径、GPU、脚本来源或沙箱隔离。命令、报告及扫描边界见作者包检查(宿主文档:../modules/package-check.md)。