name: panel-app-creator description: Create or update the Panel App UI part of a NextClaw lightweight app. Use after nextclaw-app-creator selects Panel-only or Panel + Service, or when the user explicitly asks for a right-side Panel App UI, folder-based static panel, Service Actions UI, Agent-powered Panel App, React/Vite/Tailwind Panel App, or asks what a Panel App can do and which injected APIs/capabilities it can use. description_zh: 创建或修改 NextClaw 轻量应用中的 Panel App UI 部分。适用于 nextclaw-app-creator 判断为 Panel-only 或 Panel + Service 后,或用户明确要求右侧面板 UI、目录式静态面板、Service Actions UI、Agent 驱动的 Panel App、React/Vite/Tailwind Panel App,或询问 Panel App 能做什么、能使用哪些注入 API/能力。
NextClaw Panel App Creator
当用户需要实现右侧面板 UI 时使用这个专项 skill。若用户表达的是“做一个 NextClaw 小应用”而不是明确只做 UI,先读取 nextclaw-app-creator 判断是否还需要 Service App。
如果 Panel App 要调用 window.nextclaw.serviceActions.*、window.nextclaw.agent.* 或 window.nextclaw.client,必须先读取 references/panel-app-bridge-api.md,并按该 reference 的运行时合同写代码。
本 skill 只拥有 Panel App UI、panel-app.json、bridge 调用和窄侧栏体验。它不拥有 service-app.json、actions.*.risk、MCP server、Service App command 或依赖安装规则。只要需要创建或修改 Service App 文件,必须立即读取 service-app-creator,不能凭 Panel App 文档猜后端 manifest 字段。
前端工程形态判断
先主动判断是否需要工程化 React + Vite + TypeScript + Tailwind CSS + pnpm,不要只在用户点名技术栈时才读取专项 skill。工程化推荐栈是一整套,缺一不可;如果不使用这整套,必须是轻量目录式静态面板、用户明确指定其它技术栈,或有清楚的环境约束理由。
- 适合工程化方案:AI 应用、对话/Agent Run、需要 App Client 类型、多个视图/组件、列表筛选排序、图表、复杂表单、异步加载状态、错误/空状态较多,或后续会持续维护的 Panel App。遇到这些信号,先读取
panel-app-react-vite-creator,并使用完整React + Vite + TypeScript + Tailwind CSS + pnpm栈。 - 适合轻量目录式:极小工具、一次性页面、纯展示/简单表单、少量内存状态、无明显组件拆分和构建收益的应用。不要为了“像工程”而引入 npm 工程。
- 用户明确要求 Vite、React、Tailwind、现代前端技术栈、工程化源码、可构建 Panel App,或希望用 pnpm 开发再交付静态产物时,必须读取
panel-app-react-vite-creator。
本 skill 仍负责最终 panel-app.json、bridge、Client SDK、Service Actions、窄侧栏体验和 nextclaw app check 验收规则;panel-app-react-vite-creator 只负责源码工程、Vite/Tailwind 配置、构建和静态 .panel 产物交付。
能力发现
当用户询问 Panel App 能做什么、能否接入 AI、能否读取会话/历史/Agent Run、能否上传资产或订阅事件时,必须说明 window.nextclaw.client 这类 App Client 能力,但不要把 Service Actions 迁移成 App Client 主路径。Service Actions 当前推荐继续使用旧 bridge,因为旧 bridge 拥有 Panel App 所需的授权确认和自动 retry 体验。
默认说明这些能力边界:
- Panel UI:不需要宿主授权的静态界面、本地状态、表单、列表、图表。
window.nextclaw.client:声明"client": true并整体授权后同步可用,是 App Client projection,适合 sessions、agents、agentRuns、assets、events 等标准客户端能力。client.serviceActions.*当前存在,但不要作为 Panel App Service Actions 推荐路径。- Service App:本地文件、外部 API、本地命令等后端动作;Panel App 当前推荐通过旧 bridge
window.nextclaw.serviceActions.*调用,以保留授权确认、grant 和自动 retry。
window.nextclaw.serviceActions.* 是当前 Panel App 调用 Service Actions 的推荐入口,不要因为 App Client 里存在 client.serviceActions.* 就默认替代。window.nextclaw.agent.* 暂时保留为旧 Agent bridge;只有实际不想开启整体 client 授权、维护旧应用,或需要旧 Agent bridge 独有便利能力时才提它。
输出位置
- Panel App 必须写入 NextClaw workspace 的
panels/目录。 - 默认 workspace 是
~/.nextclaw/workspace;如果当前任务能读取 NextClaw 配置,则以agents.defaults.workspace为准。 - 目录式应用的目录名必须使用 kebab-case,并以
.panel结尾,例如todo-board.panel/。
文件形态
- 新建或重写 Panel App 时只使用目录式 Panel App。
- 目录式 Panel App 必须包含
panel-app.json和入口 HTML;不要求服务器部署,不创建 npm 项目,不运行构建工具。 - 需要后端能力时,可配套
service-app-creator创建 Service App,但必须先执行 Service App 可用性检查(见下方"Service App 可用性"节)。 - Panel App 运行在 sandbox iframe 中,不能依赖
localStorage、sessionStorage、cookie 或 IndexedDB。轻量状态默认只放内存;需要保存时提供导出/导入 JSON,或配套 Service App / App Client 能力。 panel-app.json是标题、描述、图标、入口、Agent capabilities 和 Service actions 的唯一 manifest 事实源;不要把 NextClaw manifest 字段写到 HTML meta。
Sandbox 运行环境约束
Panel App 不是普通同源网页。宿主为了隔离应用,iframe sandbox 不包含 allow-same-origin,文档 origin 是 opaque / null。写代码时必须默认遵守:
- 不访问
window.localStorage、window.sessionStorage、document.cookie、IndexedDB,也不要使用会在初始化时自动触碰这些 API 的持久化库或状态插件。 - 不假设当前页面和宿主同源;不要读取父窗口 DOM、cookie、token 或宿主内部全局变量。
- 不直接持久化 bridge token、runtime token、session token 或授权状态。
- 同源
/api/...请求只能用于宿主允许的 Panel App 运行时链路;优先使用window.nextclaw.client、window.nextclaw.serviceActions.*或window.nextclaw.agent.*。 - 临时 UI 状态用普通 JS/React state;需要跨重新打开保存时,优先设计为 Service App action、App Client 标准能力,或显式导出/导入 JSON。
如果用户报告 Failed to read the 'localStorage' property from 'Window': The document is sandboxed and lacks the 'allow-same-origin' flag,根因通常是 Panel App 或其依赖在 sandbox 环境中访问了 Web Storage。修复方向是删除该持久化访问,而不是给 iframe 增加 allow-same-origin。
如果用户报告具体运行时报错,例如 Illegal invocation、sandbox storage、opaque origin、CORS 或 App Client 注入异常,先读取 references/panel-app-runtime-troubleshooting.md,按错误签名定位,不要把所有低频排障细节写进主流程。
目录式静态应用
适合需要后续扩展、持续维护、资源拆分或更清晰代码组织的静态应用:
panels/markdown-manager.panel/
panel-app.json
index.html
app.js
styles.css
assets/icon.svg
panel-app.json 示例:
{
"title": "Markdown 管理器",
"description": "浏览、编辑和整理本地 Markdown 文件",
"icon": "assets/icon.svg",
"entry": "index.html",
"capabilities": ["agent:generateObject"],
"actions": ["workspace-files.list", "workspace-files.read"]
}
目录式规则:
id可以省略,系统会使用目录名作为身份;如果显式填写id,必须等于目录名去掉.panel后的值,例如markdown-manager.panel对应markdown-manager。entry必须是目录内的 HTML 文件,通常是index.html。- 只有确实需要完整 NextClaw Client SDK 时才写
"client": true;不需要 client 的 Panel App 不要声明。 icon可以是 emoji、data URL、http/https/绝对路径,也可以是目录内相对资源路径,例如assets/icon.svg。- 相对 CSS、JS、图片路径可以直接写
styles.css、app.js、assets/icon.svg;NextClaw 会通过资源接口托管它们。 - 不要在目录式 Panel App 里创建
package.json、node_modules、Vite 配置或后台 dev server,除非用户明确要求后续升级为更重的形态。 - 创建或修改 Panel App 后不需要重启 NextClaw 宿主、server 或桌面应用;目录内容会在 workspace 中被重新读取。需要看到最新效果时,刷新“面板应用”列表、重新打开该 Panel App,或使用面板右上角刷新/重新加载内容。
启动器元信息
每个 Panel App 默认都要在 panel-app.json 写清启动器元信息,让用户在 Panel Apps 列表中能快速识别它:
{
"title": "番茄便签",
"description": "番茄钟、任务清单和专注记录",
"icon": "🍅",
"entry": "index.html"
}
图标规则:
- 最简单优先在
panel-app.json.icon放一个语义明确的 emoji 或 1-2 个短字符。 - 如果用户要求正式图片图标,再使用目录内相对资源路径,例如
assets/icon.svg。 - 不要省略图标;没有用户指定时,按应用主题选择一个克制、可识别的 emoji。
- 可以在
panel-app.json.icon中使用相对资源路径。
窄侧栏优先布局
Panel App 默认打开在 NextClaw 右侧栏里,初始宽度通常较窄;用户可以把右侧栏拉宽,但不能假设一开始就是桌面宽屏。
- 默认按
320px-480px窄面板优先设计,再逐步增强到宽屏。 - 主流程、核心按钮、表单和列表在窄宽度下必须完整可用,不横向溢出。
- 使用单列布局作为基础;宽屏时再用 CSS media query 升级为双列、网格或并排详情。
- 工具栏和筛选项在窄屏下允许换行、折叠或变成分段按钮,不要做依赖大宽度的顶部横排。
- 卡片、表格、图表要适配窄容器:长文本换行,表格优先改成列表/详情卡,图表使用
width: 100%和稳定高度。 - 不使用固定桌面宽度容器,例如
width: 960px;容器应使用max-width: 100%、min-width: 0、box-sizing: border-box。 - 验收时至少想象两种宽度:窄侧栏和用户拉宽后的面板。窄侧栏可用性优先于宽屏装饰性。
Panel Card 体验合同
只有当你已经判断这个 Panel App 适合以内联卡片展示时,才使用本节;不要把普通右侧面板、编辑器、管理页、大表格或多页工作流硬做成卡片。
适合 Panel Card 的形态:
- 天气卡片、汇率卡、计时器、计算器、checklist、picker、短表单、小 preview、小 dashboard。
- 用户希望“直接试一下”“快速看一下”“在当前对话里操作”,且核心交互能在有限高度内完成。
不适合 Panel Card 的形态:
- 长文档阅读、富文本/代码/Markdown 编辑器、文件浏览器、设置页、复杂管理台、大表格、多页持续工作区。
如果决定做 Panel Card,必须按 card-first 设计:
- 首屏
220px-420px高度内必须看见核心价值,不要让用户先滚动才知道卡片能做什么。 - 横向优先:默认让信息沿宽度展开,宽度大于高度;常见结构是左侧主结果/核心状态,右侧短列表、次要指标或一个主操作。容器太窄时再折成单列。
- 禁止横向滚动、固定桌面宽度、大片空白和过高 hero。
- 设计上不要依赖 document 级内部滚动;允许 dropdown、select、短列表等控件级局部滚动。内容超出时改成摘要 + 展开,而不是把整张卡片设计成滚动页面。宿主可能用 iframe 滚动兜底旧内容,但不要把它当成默认体验。
- 只保留一个主操作,最多 2-3 个辅助操作;复杂配置、详情和历史记录交给展开后的 side panel。
- 必须有 loading、empty、error 状态;错误要可读,不要只在 console 里报错。
- 视觉要像精美卡片:清晰层级、克制色彩、足够留白、稳定高度、数字/状态突出、按钮和输入控件可点击。
- inline 宿主会在 URL 上追加
nextclawDisplayMode=card和nextclawPlacement=inline;检测到这些参数时,优先渲染 card-first 布局,不要继续使用完整页面布局。 - 完成后用
show_content(type="panel_app", placement="inline")展示;如果实现过程中发现卡片空间不足,改用placement="side_panel",不要勉强塞进 inline。
Service Actions
本节只说明 Panel App 如何声明和调用已存在或由 service-app-creator 设计出的 Service Action;不要在这里发明 Service App action 的 risk、inputSchema、command 或 server 实现。当前 Panel App 调用 Service Actions 推荐使用 window.nextclaw.serviceActions.*;client.serviceActions.* 暂时不作为推荐路径,因为它缺少旧 bridge 的授权确认和自动 retry 体验。
Panel App 调用 Service App 前,必须在 panel-app.json 的 actions 数组声明允许使用的 action:
{
"title": "Markdown 管理器",
"entry": "index.html",
"actions": ["workspace-files.list", "workspace-files.read"]
}
action id 必须来自对应 service-app.json.actions:<service-app-id>.<tool-name>。service-app-id 不包含点号,tool name 可以包含点号。
不要发明 permissions.allowlist、serviceActions 等额外字段;当前标准字段就是 actions。
运行时通过宿主注入的 SDK 调用:
// Service App 调用必须在 try/catch 中,错误要给出明确用户反馈
try {
if (window.nextclaw?.serviceActions?.invoke) {
// invoke() 返回 action 的业务结果 payload,不需要读取 response.result
const note = await window.nextclaw.serviceActions.invoke("workspace-notes.readNote", {
path: "notes/today.md"
});
renderNote(note.content ?? "");
} else {
showError("当前环境不支持 Service Actions");
}
} catch (e) {
console.error("Service Action 调用失败:", e);
showError("Service Action 调用失败,请检查授权和服务状态。");
}
list() 返回 action 数组,invoke() 已由宿主 SDK 解包,返回值就是 Service App tool 的业务 payload。不要写 response.actions、response.result、response.files 这种不确定访问:
const actions = await window.nextclaw.serviceActions.list();
renderActions(actions);
如果 action 返回 { files: [...] },应写:
const payload = await window.nextclaw.serviceActions.invoke("workspace-files.list", {});
const files = payload.files ?? [];
错误提示要区分原因:bridge 不存在才说 Service Actions 不可用;调用抛错才说 Service Action 调用失败;返回结构不符合预期要说返回格式未识别,不要统一误报为 Service App 不可用。
不要在 Panel App 里伪造 caller、保存 token 或直接请求 Service Gateway;首次调用授权由宿主处理。 不要为了发现 action 在 Panel App 启动时自行触发后端探测;Service Actions 列表由宿主读取静态 manifest,运行时 discovery 由服务应用面板中的显式操作完成。
Client SDK
Panel App 如果需要完整 NextClaw Client SDK,在 panel-app.json 中声明:
{
"title": "示例应用",
"entry": "index.html",
"client": true
}
授权后,宿主会同步注入 window.nextclaw.client。它是面向 Panel App 的 NextClawAppClient projection,不是完整底层 NextClawClient;不要自己 import、不要自己初始化 token,也不要假设有 host/admin namespace。
App Client 不只是“发送请求的 transport”。对 AI/聊天类 Panel App 来说,NextClaw 已经是会话、消息、Agent Run、实时事件和资产的持久化 owner:
- 用稳定
peerId让 NextClaw 创建或复用会话,不要保存sessionIdByAgent这类浏览器侧映射。 - 用
client.sessions.list()/client.sessions.get()/client.sessions.listMessages()恢复历史,不要把聊天记录镜像到浏览器 storage。 - 用
client.agentRuns.send()触发运行;用返回的sessionId只作为当前内存态和事件过滤条件。 - 用
client.events.subscribe()追踪运行中的流式事件;页面重新打开后再从sessions/listMessages恢复最终事实。 - Panel App 自己只维护渲染所需的临时 UI state,例如当前输入框内容、当前选中的 agent id、正在流式累积的片段;这些状态默认放内存,不跨重新打开持久化。
const client = window.nextclaw?.client;
if (!client) {
showError("当前面板应用尚未获得 NextClaw Client SDK 授权。");
return;
}
const sessions = await client.sessions.list();
不要把 Client SDK API schema 硬编码进本 skill 或 Panel App 模板。需要类型和接口时,应从用户机器上已安装的 NPM 包解析:
- 优先在运行环境中解析
@nextclaw/client-sdk包位置,例如require.resolve("@nextclaw/client-sdk")或查看包的package.json.exports。 - 类型入口通常在包的
dist/index.d.ts或 package exports 的types指向处;构建产物里可以找到NextClawAppClient以及它引用到的 request/response type。 - Monorepo 回退路径:
@nextclaw/client-sdk是nextclaw-ui的依赖,不是顶层根依赖。如果require.resolve在默认工作区找不到,先检查相关 workspace 包的node_modules(如packages/nextclaw-ui/node_modules/@nextclaw/client-sdk/dist/),直接读dist/下的.d.ts类型声明。不要跳到源码树里搜索——编译包的类型声明比源码更准确且更易定位。 - 如果所有工作区的
node_modules都找不到该包或声明文件,再明确提示当前 NextClaw 安装缺少 Client SDK 产物,不要凭记忆猜接口。
Service App 可用性
构建 Panel App 时必须区分“环境不支持”“未授权”“服务调用失败”“返回格式不符合预期”,不要把所有问题都报成 Service App 不可用:
- 先检查,后调用:在调用任何 service action 前,检查
window.nextclaw?.serviceActions?.invoke是否存在。 - 声明一致:调用前确认
panel-app.json.actions包含实际 action id。 - try/catch 包裹:所有 service action 调用必须在 try/catch 中,catch 里展示明确错误。
- 返回值校验:
invoke()返回业务 payload;如果 payload 结构不符合 UI 预期,提示“返回格式未识别”,不要误报为连接失败。 - 本地降级有边界:只有用户目标允许页面内临时状态时才做纯前端降级;读写文件、执行命令、外部 API、稳定持久化这类目标不能静默降级为假成功。
- 运行时失败快速决策:如果 Service App 已部署但返回错误,最多调试 2 次(检查 manifest、检查 server 日志),仍失败就明确告知用户当前阻塞点,不要反复空转。
- 多文件写入后做交叉一致性检查:CSS/JS/HTML 分开写时容易出现类名、ID、事件名不一致。全部文件写完后、交付前,快速检查:(a) JS 中的
querySelector/className是否在 CSS 中有对应规则;(b) HTML 中的id/class是否在 JS 中被引用;(c) CSS 选择器是否覆盖了 JS 动态生成的 DOM 结构。不要依赖浏览器调试工具发现这些问题——它们在写入阶段就能通过代码审读发现。 - Panel App 不工作时先自审代码,再用外部工具:用户报告"没看到"/"没反应"时,第一反应应该是读自己写的代码(manifest、JS API 调用格式、bridge 声明),而不是立即用 Chrome DevTools 等外部调试工具。先按清单自查:(a)
panel-app.json是否声明了需要的client/capabilities/actions;(b) JS 中的 bridge/SDK 调用格式是否正确;(c)waitForClient/初始化逻辑是否有静默失败。用户给出方向性提示时(如"你是不是 api 格式没搞对"、"有没有声明 client: true"),优先验证用户指出的方向,不要继续自己的推理链。
交付前自检
新建或修改 Panel App 后,必须运行:
nextclaw app check ~/.nextclaw/workspace/panels/<app-id>.panel
nextclaw app check 失败时必须先修复,再交付给用户。它会检查目录式 panel-app.json、入口 HTML、资源路径、Agent capabilities、Service action allowlist 以及常见 bridge 调用漏声明问题。
如果这个 Panel App 配套修改了 Service App,还必须对对应服务目录运行 nextclaw app check、nextclaw app dev,并用 nextclaw app call <service-app-dir> <action-name> --input '{}' 抽测关键 action。Panel App 自检不替代后端真实 runtime 验收。
交付说明不要要求用户 restart;除非已经验证是宿主进程异常、版本切换或进程崩溃,否则重启不是 Panel App 生效步骤。
Agent API
Panel App 可以通过宿主注入的 window.nextclaw.agent 调用 NextClaw Agent。需要 Agent 能力时,必须先在 panel-app.json.capabilities 声明 capability:
{
"capabilities": ["agent:send", "agent:generateObject"]
}
只声明实际会用到的能力:
- 只触发会话 run:声明
agent:send。 - 需要结构化对象返回:声明
agent:generateObject。 - capability 名称必须精确使用冒号形式。不要写
agent.send、agent.generateObject或泛化的agent。
generateObject
generateObject 适合 AI 分析、总结、分类、生成结构化 UI 状态:把当前 UI 状态交给持续 Agent 会话,并拿回可以直接写入应用状态的 JSON object。不要为了 AI 分析新建 Service App 自己调用模型,除非用户明确要求接入某个外部模型服务。
if (!window.nextclaw?.agent?.generateObject) {
throw new Error("当前环境不支持 NextClaw Agent API");
}
const result = await window.nextclaw.agent.generateObject({
peerId: "mood-summary",
prompt: "总结这些心情记录,并给出三个具体建议。",
context: { entries },
schema: {
type: "object",
properties: {
summary: { type: "string" },
suggestions: {
type: "array",
items: { type: "string" }
}
},
required: ["summary", "suggestions"],
additionalProperties: false
}
});
关键约定:
peerId由 Panel App 自己生成并保持稳定;同一个 Panel App + 同一个peerId会复用同一个 Agent 会话。- 不要自己生成、缓存或猜测稳定
sessionId;需要固定会话时只传peerId,由 NextClaw 内部创建或复用 session。 schema必须描述期望返回对象;不要要求 Agent 在自然语言里输出 JSON。- 返回值就是结构化对象本身,不是字符串,也不是
{ result: ... }envelope。 - 第一次调用受保护 Agent capability 时,宿主会负责弹授权,不要自己实现授权 UI。
Capability 声明格式
generateObject 和 send 各自需要独立声明。声明必须使用完整格式(agent:generateObject、agent:send),不能用笼统的 agent:
{
"capabilities": ["agent:generateObject", "agent:send"]
}
错误示例:
{
"capabilities": ["agent"]
}
send
send 适合只触发一次 Agent 会话 run,不等待最终回复:
const handle = await window.nextclaw.agent.send({
peerId: "daily-analysis",
content: [{ type: "text", text: "请根据当前数据生成一份后续分析。" }]
});
后续继续使用同一个 peerId 即可投送到同一会话:
await window.nextclaw.agent.send({
peerId: "daily-analysis",
content: [{ type: "text", text: "继续上一轮,补充风险点。" }]
});
如果你已经明确拿到了已有会话的 sessionId,也可以把它作为历史 continuation 使用:
await window.nextclaw.agent.send({
sessionId: handle.sessionId,
content: [{ type: "text", text: "继续上一轮,补充风险点。" }]
});
不要同时传 peerId 和 sessionId。这两个字段分别代表“由系统按稳定 peer 创建/复用会话”和“继续一个已知会话”,混用会被拒绝。
不要在 panel-app.json.capabilities 未声明对应 capability 时调用 window.nextclaw.agent.*。调用失败时展示明确错误,不要静默当作成功,也不要退回到直接请求某个本地 Agent HTTP 接口。
实现建议
- 信任 write_file 结果:
write_file返回成功即表示文件已写入。不要用exec ls -la、head或read_file去"验证"刚写入的文件——这会浪费工具调用,并可能因批量执行的时间戳不一致导致分析循环。需要验证时,只在全部写入完成后的验收阶段做一次。 0b. manifest 只写panel-app.json:标题、描述、图标、Service actions、Agent capabilities 都写入panel-app.json。不要在 HTML<head>中添加 NextClaw manifest meta,也不要发明permissions.allowlist、serviceActions等字段。 - 明确用户要解决的实际工作流,不把临时工具做成复杂产品。
- 生成完整可打开的 HTML:
<!doctype html>、<meta charset="utf-8">、响应式布局、可访问的按钮和输入。 - 每次新建或重写 Panel App 时,都要先补齐
panel-app.json的title、description、icon和entry。 - 本地状态优先用内存变量或框架 state;需要导入导出时,用文本框、复制、下载 JSON 等浏览器原生能力。不要使用
localStorage、sessionStorage、cookie 或 IndexedDB。 - 视觉保持克制、清晰、信息密度适中,避免营销页式 hero 和装饰性堆叠。
- 完成后告诉用户在 NextClaw 左下角设置菜单打开“面板应用”,再选择对应应用。