跳到主要内容

iframe 快速入门

这是无需先构建后端即可试用开发者 API 的最快方法。

在 iframe 模式下,您的应用通过 postMessage 与 Chastify 父应用通信,而 Chastify 代表您调用服务器 API。

Chastify 会将什么传递给你的 iframe

打开时,Chastify 将 JSON 有效负载放入 location.hash 中。
重要字段:

  • bridge.nonce:桥接消息的请求签名值。
  • bridge.parentOriginpostMessage 所需的目标来源。
  • sessionId:此锁的稳定扩展会话 ID。
  • lockId:活动锁 ID。
  • ui:来自父页面的主题值。
  • 可选:homeActionSlughomeActionintentregularActionsSummarymainToken

使用 apps/extension/src/lib/ChastifyBridge.ts 中的辅助函数:

  • parseHashPayload()
  • ChastifyBridgeClient
  • startAutoResizeToParent(...)
  • themeVars(...)

极简 React Bootstrap

import { useEffect, useMemo, useState } from "react";
import {
parseHashPayload,
ChastifyBridgeClient,
startAutoResizeToParent,
themeVars,
} from "../lib/ChastifyBridge";

const payload = parseHashPayload();
if (!payload?.bridge?.nonce || !payload?.bridge?.parentOrigin) {
throw new Error("Missing bridge payload in iframe hash");
}

export function App() {
const [client, setClient] = useState<ChastifyBridgeClient | null>(null);
const [session, setSession] = useState<any>(null);
const [stateDoc, setStateDoc] = useState<any>(null);
const vars = useMemo(() => themeVars(payload.ui ?? null), []);

useEffect(() => {
const c = new ChastifyBridgeClient({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
});
setClient(c);
return () => c.destroy();
}, []);

useEffect(() => {
if (!client) return;
(async () => {
const s = await client.request("session.get", {});
setSession(s);

const st = await client.request("state.get", {});
setStateDoc(st);
})().catch(console.error);
}, [client]);

useEffect(() => {
return startAutoResizeToParent({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
extraPx: 12,
});
}, []);

return (
<div style={{ background: vars.pageBg, color: vars.text, minHeight: "100%" }}>
<h2>Extension Quickstart</h2>
<pre>{JSON.stringify(session, null, 2)}</pre>
<pre>{JSON.stringify(stateDoc, null, 2)}</pre>
</div>
);
}

原始 postMessage 示例(无辅助类)

如果您不使用 ChastifyBridgeClient,请手动发送完整的请求信封:

const req = {
type: "chastify:ext:req",
v: 1,
id: crypto.randomUUID(),
nonce,
action: "session.get",
payload: {},
};

window.parent.postMessage(req, parentOrigin);

留意是否有匹配的回应:

window.addEventListener("message", (event) => {
if (event.origin !== parentOrigin) return;
const msg = event.data;
if (!msg || msg.type !== "chastify:ext:resp" || msg.v !== 1) return;
if (msg.id !== req.id) return;

if (msg.ok) console.log("Bridge success:", msg.data);
else console.error("Bridge error:", msg.error);
});

首先应该运行的测试流程

  1. session.get
  2. state.get
  3. metadata.get
  4. regularActions.get

这确认了桥接身份验证、安全读取以及请求/响应处理。状态写入必须由您的后端使用开发者 API 凭据执行。

从 iframe 上传文件

如果您的扩展程序需要图像或生成的媒体,请使用桥接文件助手。Chastify 会将文件上传到托管的 R2 存储,返回一个稳定的文件 ID,并提供一个用于渲染的短期签名 URL。

const capabilities = await client.filesCapabilities();
if (capabilities.enabled) {
const uploaded = await client.filesUpload({
file,
filename: file.name,
purpose: "puzzle-image",
});

// Send uploaded.file.id to your backend if it should be stored in session state.

const refreshed = await client.filesGet(uploaded.file.id);
image.src = refreshed.file.signedUrl;
}

存储 uploaded.file.id,而不是已签名的 URL。已签名的 URL 会过期,当 iframe 再次加载时,应使用 files.get / client.filesGet(...) 刷新。

主题支持(ui + themeVars

Chastify 在 iframe 哈希中传递 ui 对象。
themeVars(ui) 将其转换为可用的设计令牌:

  • pageBg
  • text
  • panel
  • border
  • muted
  • inputBg

使用这些值作为扩展程序的基本样式,以便您的 iframe 与宿主主题相匹配。

实时主题更新

当您的 iframe 打开时,父级可以发送 chastify:ext:ui 事件。
如果想要实时主题同步,请监听该事件并更新本地主题状态。

有关浅色/深色主题处理的详细说明、对比度示例和 Tailwind 模式,请参阅Iframe 主题

自动调整大小支持

使用 startAutoResizeToParent(...),以便父元素可以根据您的内容调整 iframe 的大小。

重要性:

  • 防止 iframe 内出现滚动陷阱。
  • 保持设置模态框和扩展页面大小正确。
  • 适用于可展开/折叠的动态部分。

常见错误

  • targetOrigin 错误(必须与 bridge.parentOrigin 匹配)。
  • nonce 缺失或过期。
  • 卸载 ChastifyBridgeClient 时不调用 destroy()
  • 未先检查 session.get 功能,就发送不支持的操作。

参考文件

  • apps/extension/src/lib/ChastifyBridge.ts
  • apps/extension/src/pages/MainPage.tsx
  • apps/extension/src/pages/SetupPage.tsx