TimeLens 第三方小组件开发指南

本文档介绍 Kernel 与 Gateway 重写后的当前第三方小组件运行时。


当前范围

首批支持沙箱 JavaScript/TypeScript、ESM 入口、createWidget()/mount()、Gateway 查询、待办写入、小组件状态、订阅、本地 API、同意提示、权限撤销、限流、生命周期和审计。v1/v2 清单仍通过兼容适配器可用。

远程分发、Java Host、完整网络/媒体代理尚未提供;client.fetch() 与 client.loadMedia() 是预留 API。

目录与 Manifest v4

widgets/
  my-widget/
    manifest.json
    index.js
    assets/
{
  "manifest_version": "v4",
  "widget_type": "sample_hello",
  "name": "Sample Hello Widget",
  "publisher": "Example Publisher",
  "entry": "index.js",
  "runtime": { "language": "javascript", "version": "ES2022", "entry": "index.js" },
  "ui": { "model": "web-sandbox" },
  "capabilities": ["metrics.summary.read"]
}

v4 要求版本、唯一类型、名称、发布者、运行时语言/版本/入口、UI 模型和能力数组。当前 JavaScript 加载器还要求顶层 entry,应与 runtime.entry 一致。尺寸、图标、签名、CSP、资源配额、能力说明、请求域名和媒体来源可选。

能力与同意

特权调用统一经过 Widget Gateway。缺少授权时返回可恢复拒绝,Host 展示同意提示;只有用户允许后才重试一次。小组件中心支持撤销单项或全部 Scope。

Scope访问范围
screen-time:read使用、专注、目标、分类、会话等查询
todo:read / todo:write读取或修改待办
browser:read浏览器活动查询
settings:write写入专注模式
local-api:call调用带 Scope 的本地 API

入口与 Context

export function createWidget() {
  let root;
  return {
    async mount(container, context) {
      root = document.createElement("div");
      root.textContent = `Hello from ${context.widgetType}`;
      container.appendChild(root);
      console.log(await context.client.query("metrics"));
    },
    unmount() { root?.remove(); root = null; },
  };
}

也支持直接导出 mount(container, context) 和可选的 unmount()。Context 包含 widgetId、widgetType、新的 client、兼容用 channel 和生命周期回调。新组件应使用 client,channel 仅用于迁移。

WidgetClient

const metrics = await context.client.query("metrics", { start, end });
const value = await context.client.getState("selected-range");
await context.client.setState("selected-range", "today");
const todo = await context.client.addTodo("Review screen time");
const handle = await context.client.subscribe("focus-started", console.log);
await context.client.unsubscribe(handle);

命名空间为 metrics、sessions、categories、projects、tags、goals、rules、focus、todos、browser。本地 API 仍可使用 context.channel.localApiCall({ method, path, scopes })。

错误、生命周期与测试

Gateway 状态包括 success、denied、revoked、throttled、timed_out、degraded、error。WidgetGatewayError 提供 code、scope、recoverable,可用 isConsentRequired() 判断同意失败。请在 unmount 中释放订阅;当前每个实例每分钟最多 60 次 channel 请求。

将目录放入应用数据 widgets,以开发模式启动,打开小组件中心添加并测试同意提示和权限矩阵。开发模式提供“小组件开发调试台”时,可模拟 Gateway 响应并重新加载本地入口。

阅读英文指南 · 参阅 v1/v2 迁移说明 和 src-tauri/widget-contract/manifest-v4.schema.json。


最后更新:2026-08-27 · TimeLens 小组件运行时重写