关于 building-canvases
面向在 PostHog 中创建独立应用的技能,指导选择、编辑和发布运行于沙盒 iframe 的 freeform canvas。它还规定何时加载 React、HTML、数据查询和发布验证等配套技能。
主要功能
- 01按请求定位或创建 canvas
- 02通过工具读取和编辑 canvas 源码
- 03根据任务选择 React 或 HTML 实现
- 04为可编辑组件配置参数
- 05验证、发布并处理构建结果
适用场景
- 01在 PostHog 中创建数据看板或分析工具
- 02制作表单、清单或轻量工作流
- 03编写文档或叙述式报告
- 04构建生成图形或动画
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ posthog/ ai-plugin --skill building-canvases安装后在 Claude Code 中输入/building-canvases确认已加载。
附带文件references/blocks.md
详细介绍
构建画布
画布是在 PostHog 内部沙盒化 iframe 中运行的客户端浏览器应用。
其源代码存放在 PostHog 中,而非代码仓库里;你需要通过
canvas-* 工具读取和写入。切勿将画布写入本地文件;通过工具发布才会保存内容。
画布任务可以从任何常规任务开始。不需要专门的画布模式或预先创建画布。当用户要求创建 一个要放在 PostHog 中的看板、文档、表单、可视化内容或小型应用时,应将其视为画布请求并遵循 本技能。
本技能负责 freeform 画布(独立应用)。另外还有两种画布类型:grid
画布(小组件网格,包括用户的主页画布)和 component 画布(可供网格放置的可复用小组件)。
如果目标是网格或主页画布、放置操作,或可复用小组件/组件,请改为加载 composing-grid-canvases;
它负责管理从搜索资源库 → 配置 → 分叉 → 构建的流程,以及布局补丁循环。编写组件源代码仍需使用下面的实现配套技能。
确定目标画布
- 如果任务指定了画布 ID(由画布发起的任务会指定),则该画布就是目标。不要另建画布。
- 否则,目标频道就是创建任务时所在的频道——任务上下文(
channel_context块或生成指令)中会注明。 使用canvas-list列出该频道的画布(通过channel指定范围)。如果其中有一个明显符合请求——例如同一看板或工具的早期版本—— 就在其基础上构建,而不是创建一个近似重复项;并在回复中说明这一点,让用户知道结果保存在哪里。 - 只有在没有合适的现有画布时,才在同一频道中使用
canvas-create创建一个,并根据请求取一个简短且有描述性的标题——切勿使用“未命名画布”。 - 切勿自行遍历频道来选择目标:仅当用户指定了频道、需要将其解析为 ID 时,才使用
channel-list。其列表会将个人#me频道排在首位,而#me绝不是默认选项——保存在此处的画布对其他人不可见。如果任务既未指定画布,也未指定频道,请询问应使用哪个频道,而不要猜测。
加载实现所需的配套技能
本技能负责画布选择和编写生命周期。配套技能包含实现规范。编写源代码前,加载所有适用的配套技能:
building-react-quill-canvases用于仪表板、数据看板、表单、工具、带有应用状态的内容,或任何应采用 PostHog 原生外观的内容。它负责管理允许导入的内容、Quill 组合、主题、图表、加载和错误状态,以及日期选择器。building-html-canvases用于文档、文章、专注型实验、生成式图形、<canvas>或 WebGL 工作,适用于应用组件无法提供有用结构的场景。它负责管理语义化标记、直接浏览器 API、动画清理和非 Quill 主题。querying-canvas-data适用于任何读取 PostHog 数据、捕获事件或进行导航的画布。它负责管理phSDK、已保存洞察的优先使用、结果形状、变量、日期范围、逐查询渐进式加载,以及已声明的数据功能。涉及数据时,应与任一实现技能一同加载。validating-and-publishing-canvases适用于所有画布。它负责管理项目结构、功能声明、验证诊断、受保护的发布、草稿、构建和冲突恢复。
适当时可以混合使用不同的实现方式:由 React 负责应用框架,由浏览器图形代码负责画布元素;或者在基本静态的页面中挂载一个交互式区域。
这是一个判断问题,而不是持久化模式——只有当选项会影响你无法推断的用户可见要求时,才向用户询问。
区块
画布可以包含预制区块:数字、目标、趋势、排行榜、漏斗、留存、已保存的洞察、实时事件流、SQL 表格、筛选器、比较和刷新控件,以及标注。
它们是 src/blocks/ 中的 React 组件,用户可以在桌面应用中将其拖入画布,因此也是你源码的一部分。
更改含有 src/blocks/runtime.tsx 的画布前,请先阅读 references/blocks.md。
参数
将用户可能想要更改的值公开为参数。这样,他们就能在桌面编辑器中更改这些值,而无需创建新任务。 参数是可选的,但请为每个包含值得更改的值的面板、卡片或工具添加参数:标题、文案、事件、属性、阈值、限制、颜色。 参数需要 React 组件。纯 HTML 画布没有参数。
编辑器如何使用 editable()
在桌面编辑器中,用户可以选择画布上的任意元素:标题、段落、区块。他们可以移动、复制或删除元素,也可以双击文本进行修改。无需调用 editable() 即可实现这些操作。
editable() 会将组件设为一个区块。编辑器随后会将该区块视为一个整体:
- 单击会选中整个区块,而非区块内的元素。
- 移动、复制和删除操作会作用于整个区块。
- 侧边栏会将区块的参数显示为字段。更改后,新值会写入源码中该区块的 props。
- 要访问区块内的元素,用户可以双击区块将其打开。
因此,请在用户认为是一个整体、并会作为一个整体进行配置的部分上使用 editable():例如卡片、图表、表格、表单。周围的页面布局则保留为普通 JSX。这样,用户既可以将卡片作为区块选中,也可以直接选中页面标题或区块。如果包含整个页面的组件使用了 editable(),整个页面就会成为一个区块,每次单击都会选中整个页面。
添加参数
- 将每个面板放在单独的组件中。组件通过 props 接收这些值,并为其设置默认值。
- 在组件的根元素上展开来自
@posthog/canvas-sdk的editable(name, props, params)。name是组件名称。params将每个 prop 名称映射到对应字段。 - 使用带字面量 props 的组件,例如
<SignupFunnel blockId="b-k3m9x2" title="Signup" windowDays={14} />。编辑器会将更改写入这些 props,因此不要在行内计算它们。 - 为每次使用提供唯一的
blockIdprop,例如<SignupFunnel blockId="b-k3m9x2" … />,这样用户就能指定某张卡片让 Agent 进行操作。只能使用字母、数字、_、-、.和:,否则编辑器会忽略该 ID。在列表中,根据条目的稳定键生成 ID:``blockId={kpi-${item.id}}`。
import { editable } from '@posthog/canvas-sdk'
export function SignupFunnel(props: { blockId?: string; title?: string; steps?: string[]; windowDays?: number }) {
const { title = 'Signup funnel', steps = ['$pageview', 'signed_up'], windowDays = 14 } = props
return (
<Card
{...editable('SignupFunnel', props, {
title: { type: 'text', label: 'Title', default: 'Signup funnel' },
steps: { type: 'events', label: 'Steps', default: ['$pageview', 'signed_up'] },
windowDays: { type: 'number', label: 'Conversion window (days)', min: 1, max: 90, default: 14 },
})}
>
…
</Card>
)
}
字段类型:text、longtext、number(min、max、step;同时设置 min 和 max 时,编辑器会显示滑块)、boolean、select(options:字符串或 { value, label })、event、events、property、insight(已保存洞察的短 ID)、color。每个字段还可以包含 description。
为每个字段设置与其 prop 默认值相同的 default,例如 { type: "boolean", label: "Show legend", default: true }。未设置 prop 时,编辑器会显示 default。
color 参数的默认值是 var(--primary) 之类的主题令牌,因此在用户于编辑器中选择十六进制颜色之前,画布会自动适配浅色和深色模式。
通过参数只传递 JSON 值(字符串、数字、布尔值、字符串数组)。不要将函数和查询结果放入其中。
图像
在画布中使用公开的媒体库 URL。先调用 posthog:media-images-list,并将
purpose="canvas" 作为参数;如果已有合适的图像,请重复使用。
添加本地图像:
- 调用
posthog:media-image-upload-start,并提供文件名和purpose="canvas"。 - 在 shell 中,以 multipart 表单数据的形式将文件 POST 到返回的
upload_url。包含所有 返回的form_fields条目,并将文件部分放在最后。 - 调用
posthog:media-image-upload-complete,并提供返回的 id;然后使用其永久url作为图像的src。 - 将 URL 的确切源添加到
project.capabilities.network.origins。画布验证会检查此声明,发布后的产物会在其内容安全策略中使用此声明。
画布媒体 URL 是公开的,无需身份验证。切勿上传密钥、凭据、客户数据或敏感截图。图像必须小于 4 MB,且可解码为 PNG、JPEG、GIF、WebP、AVIF 或 BMP。切勿将图像字节编码为 base64 后放入工具调用中。
常见请求模式
将这些模式用作路由示例,而非固定模板:
- 产品仪表板、Web 分析看板或指标浏览器: React + Quill,加上数据查询。
- 清单、表单或轻量级工作流: React + Quill;如果需要读取 PostHog 数据、捕获事件或导航,再加上数据查询。对于清单或运行手册,请从
building-react-quill-canvases(references/checklist-example.md)中的完整示例开始——通过每个步骤的ph.state键共享团队进度。不要暗示可用 API 不支持的持久化能力。 - 文档或叙述性报告: 对于基本静态的阅读体验,使用 HTML;如果需要实时 PostHog 数据、筛选器或类似应用的交互,则使用 React + Quill,并加上数据查询。
- 生成式图形或动画: 使用 HTML 和浏览器图形 API。仅当 React 能显著简化应用状态或界面元素时才添加 React。
当任务带有 dashboard 或 web-analytics 等旧版请求模式时,请应用上述对应模式。模式仅作提示;用户的实际请求始终优先。
迭代循环
- 使用
canvas-source-retrieve读取当前源文件和版本指针。 记住current_version_id——发布时必须以它为依据进行保护。 - 使用上文选定的实现伴侣编辑项目文件。对于画布中显示的任何 PostHog 数据,
请遵循
querying-canvas-data(通过phSDK 加载已保存的洞察——绝不自行获取数据或使用自己的 PostHog 客户端),确保每个数据都可验证——由洞察支持的指标需链接到 PostHog 中已保存的洞察,临时查询需显示实际运行的确切查询,具体遵循该技能的“可验证性”部分——并且 在project.capabilities中声明每次ph调用(洞察短 ID 放在capabilities.posthog.insights中,捕获的事件放在captureEvents中,临时查询需设置inlineQueries: true,ph.agent.request需设置agentRequests: true)——宿主会在运行时强制执行这些要求,验证也会拒绝未声明的调用。 - 遵循
validating-and-publishing-canvases:根据需要多次使用canvas-validate-create进行验证,并修复所有错误级别的诊断。 - 通过发布来保存项目——发布是默认操作,会立即上线:
- 首个版本(
current_version_id为 null):使用canvas-publish-create发布完整项目,并传入expected_current_version_id: null。 - 已上线(
current_version_id已设置):仅使用canvas-edit-create提交编辑;对于文件内的更改,使用str_replace操作。 将当前已上线的current_version_id作为expected_current_version_id传入。 只有在要替换整个项目时,才使用canvas-publish-create发布完整项目。 - 仅当用户要求草稿、预览,或要求先审核再上线时,才使用
canvas-draft-create暂存草稿。 遵循validating-and-publishing-canvases技能处理诊断和冲突恢复。
- 首个版本(
- 等待构建完成——草稿和发布都会排入一个构建。发布或编辑响应中已包含
build.build_status;如果状态为ready或failed,请直接使用。否则,每隔几秒轮询一次canvas-builds-retrieve(最多约 2 分钟),直到构建状态为ready或failed。如果状态为failed,请读取构建的错误诊断,修复项目并再次保存——不要以构建失败结束任务。
每次用户要求的更改只保存一次,待画布就绪后再保存——不要每次微小编辑后都保存。用户要求草稿时,请在回复结尾说明草稿已就绪,可以预览并升级;validating-and-publishing-canvases 技能介绍了草稿 → 构建 → 预览 → 升级流程。
回复结尾需指出画布所在的频道,并使用画布工具返回的 url 字段创建链接(canvas-create、canvas-list 以及发布/源文件响应都包含该字段)。该字段是画布唯一有效的链接——切勿自行构造链接;猜测的 URL(项目页面、网页路由)无法正常访问。
运行时记忆与操作
-
ph.state— 持久键值记忆:ph.state.get(key, { scope })、ph.state.set(key, value, { scope })(null 值会删除该键)、ph.state.list({ scope })。 作用域"user"(默认值)对每位查看者都是私有的;"shared"则为每个画布保存一个值, 整个团队均可查看。请在capabilities.posthog.state中声明所用的作用域。 值为 JSON,序列化后上限为 64 KB,每个作用域最多 256 个键——请将大数据存储在 PostHog(洞察、数据仓库)中,并在此引用。切勿在状态中存放机密或查看者个人身份信息。 -
ph.actions.invoke(verb, payload)— 以查看者身份向 PostHog 写入数据。请在capabilities.posthog.actions中声明每个 verb;未声明或未注册的 verb 无法通过验证, 主机也会在运行时拒绝执行。请将操作绑定到明确的用户手势(查看者点击按钮),切勿在加载或渲染时触发。 注册表是唯一事实来源:使用canvases-actions-retrieve工具列出操作,并遵循每个 verb 的usage(载荷/结果格式、行为以及相应的确认文案),再进行绑定。 -
ph.tools.read(tool, args)和ph.tools.call(tool, args):以查看者身份运行允许列表中的 PostHog MCP 工具,例如读取功能标志并更改其推出比例。请在capabilities.posthog.tools中声明每个 工具。read接受只读工具;call会更改数据,因此请将其绑定到点击操作,主机会要求查看者确认。 有关允许列表以及写入标志所需的版本处理,请参阅querying-canvas-data。 -
ph.connectors.call(provider, tool, args)— 在查看时使用查看者自己的连接读取第三方实时数据 (GitHub 或任何 MCP 存储服务器)。切勿自行调用 GitHub、Calendly 或其他服务,再将结果粘贴到源代码中: 发布时该快照会过时,而且所有查看者都会看到作者的数据。请在capabilities.connectors中声明每个 provider 和工具;使用canvas-connectors-retrieve工具发现它们。有关结果和未连接时的处理,请参阅querying-canvas-data。 -
ph.agent.request(prompt)— 在查看者批准后,请画布的创作 Agent 进行更改。请在capabilities.posthog中声明agentRequests: true。仅在直接点击或提交表单时调用——主机会显示 完整提示词,并在消耗计算资源前征求查看者同意;渲染、挂载或轮询期间发起的调用会被拒绝。Agent 会将更改 发布为新版本;非创建者的请求会提交到创作任务的讨论串中,而不会启动一次运行。
源项目结构
- 保留
index.html,将其作为源工具返回的入口外壳。 src/canvas.tsx仍是常规的 React 入口组件,但可以从项目中导入其他相对路径的 TypeScript、TSX、JavaScript、JSON、SVG、CSS 和已准入的资源文件。- 可以使用
./worker.ts?worker导入独立模块 Worker。Worker 不得导入其他本地模块。 - 图片请使用上文所述的公共媒体库流程。其他二进制资源应以 base64 内容和已准入的内容类型放在项目的
assets映射中。支持 WOFF/WOFF2、WebAssembly 和通用 octet-stream 资源。 - 请完整保留平台依赖映射,不要添加 npm 包;本地相对路径导入对应项目文件,而裸导入仍仅限于平台固定的集合。