# FlyonDeckWidget

在超级块 HTML 中加载页面 SDK。脚本由飞块提供，加载后通过 `window.FlyonDeckWidget` 使用。

```html
<script src="/api/plugin-sdk/widget-page-v1.js"></script>
```

## version

只读值 `1`，表示页面桥接协议版本。

## onState()

订阅状态快照。注册监听器时，如果**当前页面 SDK 已收到宿主快照**，会立即用本页缓存调用一次；之后每次收到宿主快照时继续调用。注册本身不会查询插件后台，也不保证缓存与后台当前业务值一致。

页面晚挂载时，不应仅依赖这次回放完成首屏同步。首份状态可能为空，零值也不能证明数据已校准；`getState()` 读取的是同一份页面缓存，不能代替主动请求。推荐在已放置实例收到首份上下文后调用一次 `requestData()`，并防止延迟响应覆盖后续推送，见[超级块首屏读取](../guides/data-services.md#超级块首屏读取)。

```ts
FlyonDeckWidget.onState(listener: (state: unknown | null, context: WidgetPageContext) => void): () => void
```

### 参数

| 名称 | 类型 | 说明 |
| --- | --- | --- |
| `listener` | `function` | 接收当前状态和页面上下文。尚无有效状态时，`state` 为 `null`。 |

### 返回值

返回取消订阅函数。页面不再需要监听时，调用这个函数。

### 示例

```js
const unsubscribe = FlyonDeckWidget.onState((state, context) => {
  document.querySelector("#count").textContent = state ?? "—";
});
```

## getState()

同步读取最新状态。

```ts
FlyonDeckWidget.getState(): unknown | null | undefined
```

### 返回值

返回最后收到的状态值。首个快照到达前为 `undefined`；快照中没有有效状态时为 `null`。

## getSettings()

读取当前实例的公开配置。

```ts
FlyonDeckWidget.getSettings(): Readonly<Record<string, string | number | boolean>>
```

### 返回值

返回配置对象。私密配置由后台读取，页面中不包含这些字段。

## getContext()

读取页面所在的插件、超级块类型和显示模式。

```ts
FlyonDeckWidget.getContext(): WidgetPageContext
```

### 返回值

返回下面定义的 `WidgetPageContext`。

## requestData()

向清单中声明的数据提供者请求一次数据。这个调用用于已放置的实例，桌面布局预览和设备页面均可调用；目录中尚未放置的组件缺少真实实例，不能发起实例数据请求。不要仅根据 `mode === 'preview'` 跳过请求。

```ts
FlyonDeckWidget.requestData<Result = unknown>(providerId: string,
  input?: Record<string, unknown>): Promise<Result>
```

### 参数

| 名称 | 类型 | 说明 |
| --- | --- | --- |
| `providerId` | `string` | 本超级块对应的数据提供者 ID。 |
| `input` | `Record<string, unknown>` | 符合 `requestSchema` 的输入对象，默认 `{}`。 |

### 返回值

Promise 解析为后台提供者的返回值。页面最多等待 35 秒；提供者的执行时间另由 `timeoutMs` 设置。

### 示例

```js
const result = await FlyonDeckWidget.requestData("refresh", {});
document.querySelector("#temperature").textContent = result.temperature;
```

## invokeAction()

提交本超级块声明的实例操作，例如计数器的“增加”。这个调用用于已放置的实时实例。

```ts
FlyonDeckWidget.invokeAction(actionId: string, args?: Record<string, unknown>): void
```

### 参数

| 名称 | 类型 | 说明 |
| --- | --- | --- |
| `actionId` | `string` | `instanceActions` 中声明的 ID。 |
| `args` | `Record<string, unknown>` | 符合该实例动作 `argsSchema` 的参数，默认 `{}`。 |

### 返回值

无返回值。操作完成后，由后台发布状态，页面通过 `onState()` 更新界面。

### 示例

```js
document.querySelector("#increment").onclick = () => {
  FlyonDeckWidget.invokeAction("increment", {});
};
```


## WidgetPageContext

| 属性 | 类型 | 说明 |
| --- | --- | --- |
| `surface` | `Readonly<WidgetSurfaceGeometry> \| null` | 宿主裁剪轮廓在 iframe 内的 CSS 像素坐标；首个有效快照前为 `null`；状态快照必须携带有效几何信息 |
| `mode` | `"preview" \| "live" \| null` | 展示环境：桌面目录和布局预览为 `preview`，设备页面为 `live`；首个快照前为 `null`。不表示是否有真实数据或是否允许操作 |
| `pluginId` | `string` | 当前插件 ID |
| `widgetId` | `string` | 超级块类型 ID |
| `settings` | `Readonly<Record<string, string \| number \| boolean>>` | 当前实例的公开配置 |

需要首次进入时显示完整界面，可以在 `onState()` 中同时读取状态和配置，见[创建超级块](../guides/widgets.md)。

桌面端同样应优先显示真实状态，只有确有条件限制无法同步时才显示标明“示例”的内容。详见[桌面端与移动端数据同步的最佳实践](../guides/widgets.md#最佳实践一-桌面端与移动端数据同步)。


## WidgetSurfaceGeometry

宿主外轮廓映射到 iframe 内、未变换的 CSS 像素坐标。桌面目录卡片、布局预览和设备端实时页面均提供；宿主尺寸变化时随状态上下文更新。不要把外部画布的缩放或 iframe 绘制层的缩放再应用一次。

| 属性 | 类型 | 说明 |
| --- | --- | --- |
| `insetX` | `number` | 左右裁剪边界相对 iframe 边缘的内缩量，CSS 像素 |
| `insetY` | `number` | 上下裁剪边界相对 iframe 边缘的内缩量，CSS 像素 |
| `cornerRadiusX` | `number` | 宿主裁剪圆角的水平半径，CSS 像素 |
| `cornerRadiusY` | `number` | 宿主裁剪圆角的垂直半径，CSS 像素 |

四个值均为非负有限数值，SDK 提供只读快照。由于绘制层可能分别沿两个轴放大，水平和垂直半径可能略有差别。它们用于内部装饰的对齐，不要求插件自行裁剪根元素。完整布局原则见 [超级块圆角处理的最佳实践](../guides/widgets.md#最佳实践二-外轮廓圆角与几何裁剪)。

## getControlValue()

仅用于声明 `valueControl: true` 的数值控件。无参数，返回 Promise，解析为 `{minimum, maximum, step, value, available, title, icon?, updatedAt?, message?}`。value 为目标单位中的实际值；null 表示没有有效反馈，available 表示当前能否控制。message 是可显示给用户的不可用原因。应在首个 onState 快照后调用，按需刷新；内置滑块每 1.5 秒读取一次。该查询只读取宿主缓存，不轮询目标插件的业务接口。

## setValue()

`FlyonDeckWidget.setValue(value: number): void` 提交有限数字。目标动作必须显式声明 Action.valueControl；不满足该声明的绑定会停用并提示重新选择。宿主读取真实实例保存的绑定并校验数值范围和目标参数，再通过统一动作执行器执行；页面不能选择目标或固定参数。内置滑块在松手或键盘提交后调用，避免高频写操作。实际结果由宿主现有动作结果通道报告，反馈可随后通过 getControlValue 读取；返回 void 不表示操作已完成。

## getLocale()

返回当前宿主传来的语言，值为 `zh-CN` 或 `en-US`。Web Client 的语言可独立于桌面端；收到后续语言快照时更新。

## t()

`t(key, values?)` 使用当前插件的包内词典翻译语义键并格式化 ICU 参数。先加载 `/api/plugin-sdk/i18n-v1.js`；资源由宿主在页面脚本执行前自动提供。缺失消息依次回退到插件默认语言和原始键，不查询宿主词典。详见[国际化](./i18n.md)。

## getIcon()

只读获取宿主图标，需要宿主能力 `widgets.icons: 1`。在收到首个页面快照后调用：

```ts
FlyonDeckWidget.getIcon(id: string): Promise<ResolvedIcon | null>
```

支持内置图标 ID（如 `host:filled/Sunny`）及宿主返回的包内 SVG data URI，返回静态 SVG，保留 `currentColor`。不存在的图标返回 `null`。无需权限或插件数据提供者声明；不支持任意路径和外部 URL。

`getControlValue()` 返回当前控制动作的 `title` 和可选 `icon`，可将 `icon` 交给 `getIcon()`，在页面中显示所选动作图标。

## onControlValue()

```ts
FlyonDeckWidget.onControlValue(listener: (snapshot: Readonly<WidgetControlSnapshot>) => void): () => void
```

订阅用户已配置的控件目标，支持插件动作和宿主自定义数字状态。返回取消订阅函数。SDK 在收到页面上下文后读取，每秒最多刷新一次，合并重复快照；目标不可用时快照的 `available` 为 `false`。插件可通过 `value`、`minimum`、`maximum`、`step`、`title` 和可选 `icon` 渲染任意数值控件。

声明 `valueControl: true` 的插件超级块使用同一套 `getControlValue()` / `setValue(value)` 接口。状态目标的选择和访问校验由宿主负责，页面不接收也不能提交任意状态标识。`widgets.stateControl: 1` 标识此宿主能力。
