空间内嵌应用:应用模板
Knodo 应用模板机制:把工作空间的「形态」沉淀为可复用的模板,AI 辅助开发,一键分发给团队
应用生态
Knodo 应用生态(App Ecosystem)让你把一个工作空间的「形态」 —— 包括侧边栏布局、自定义面板、业务视图 —— 沉淀成可复用的模板,然后一键分发给团队其他人。
比起从零搭建一个新空间,基于 App 模板创建的空间开箱即用:侧边栏已经按业务流程排好,自定义面板(比如"客户列表"、"销售漏斗")直接显示业务数据,团队成员打开即用。
核心概念
什么是 App(应用模板)
App = 工作空间的形态。它描述了:
- 侧边栏布局:显示/隐藏哪些内置面板(任务、知识、变更等),追加哪些自定义面板
- 面板组件:自定义面板的 UI 是什么样、放哪些数据
- 文案重命名:把「知识」改为「代码」、把「任务」改为「跟进」之类
- 元数据:名称、版本、图标、作者、分类
一个 App 可被多个工作空间同时绑定,每个空间独立拥有自己的数据,但 UI 形态完全一致。
App vs Plugin 的区别
Knodo 平台现有两个扩展机制,分工清晰:
| 维度 | Plugin(插件) | App(应用模板) |
|---|---|---|
| 解决什么 | AI 能做什么 | 空间长什么样 |
| 组成 | Agent 角色、Skill 技能、命令、Hook | 面板、侧边栏、布局 |
| 绑定关系 | 多对多(一个空间装多个插件) | 一对一(一个空间用一个 App) |
| 运行时 | AI 对话期间调用 | 打开空间就生效 |
两者独立运作:一个基于「销售 CRM」App 创建��空间,可以同时装 Javis 插件跟 AI 对话、装 Pagecraft 插件生成页面。
内置 App
平台预置两个 App 模板:
- 通用(general):默认布局——知识库 + 任务 + 对话三栏,大多数场景开箱即用。老工作空间自动绑定此 App
- 应用模板开发(app-dev):平台内 AI 辅助开发应用模板的专用模板。在空白工作空间的设置中切换到此 App 后,一句话描述需求就能生成面板 → 实时预览 → 发布到应用模板库。详见应用模板开发章节
使用流程
1. 发现和使用
从左侧导航进入 能力中心 → 应用。页面内分为两个 Tab:
- 自建应用:本组织上传或发布的组织级应用,默认已对当前组织可用,不需要再安装。上传入口「上传组织应用」也在这个 Tab 内。
- 应用市场:平台内置应用和可安装的公开应用。平台内置应用(如「通用」「应用模板开发」)会标记为「平台内置 / 默认可用」,不展示安装按钮;非内置市场应用需要先安装到当前组织,才可被本组织工作空间使用。市场列表只展示当前组织自己的安装状态,不展示其他组织的安装数量。
组织管理员或应用创建者可以打开 App 详情,可以看:
- 面板定义:这个 App 有哪些自定义面板
- API 代理:这个 App 是否声明了可被面板调用的 App API Proxy endpoints
详情页中的编辑、重新上传 ZIP 等管理操作只对当前组织内有权限的成员展示。公开应用来自其他组织时,可以查看和安装到当前组织,但不会显示这些管理按钮。
2. 为现有空间切换 App
空白新建工作空间时不选择应用模板,默认使用「通用」布局;基于空间模板创建时,新空间会继承该空间模板绑定的 App。创建完成后,可进入空间 → 设置 → 应用模板 Tab 切换:
- 看到当前绑定的 App 信息
- 点 「切换模板」 展开选择器
- 确认后侧边栏立即按新 App 配置重新渲染
- 空间内容(知识库、任务、对话记录)不受影响,只是「外壳」变了

3. App 布局生效规则
切换 App 后:
- 侧边栏、内置面板隐藏、面板重命名、自定义面板按 App manifest 渲染
- 空白创建且未切换 App 的空间使用内置「通用」布局
- 基于空间模板创建的空间默认继承模板绑定的 App
- 工作空间创建流程保持轻量,手动 App 选择集中在空间设置里完成
应用模板开发
为什么这么做
传统做法开发��个前端应用要:搭脚手架 → 选组件库 → 写代码 → 调样式 → 打包 → 部署。对非程序员用户,这条路基本走不通。
Knodo 的做法是把开发环境做成一个工作空间,让 AI 完成所有技术细节:
- 知识库(= 代码目录):AI 把源码写在这里
- AI 对话:你用自然语言提需求、改样式、加功能
- 预览面板:浏览器端编译 + 实时预览,所见即所得
- 一键发布:AI 自动打包上传
整个过程你只需要:想清楚要什么 + 看预览说满意还是不满意。
创建开发空间
先新建一个空白工作空间。新建流程不需要选择应用模板,创建完成后进入空间 → 设置 → 应用模板 → 切换为 「应用模板开发」。
进入后左栏是 预览 面板,默认展开;还能看到 代码 面板(其实是知识库,专门为此模板重命名)。

跟 AI 说需求
在对话里描述你要做什么,越具体越好:
/javis:app-developer 做一个电影收藏管理应用,要能新增电影(名字、年份、评分)、按年份筛选、按评分排序
AI 会:
- javis 插件内置了一个 app-developer 的 skill,他知道如何构建应用模板
- 根据需求推导 slug(例如
movies) - 写
apps/movies/app.yaml定义面板和布局;需要开发预览专用配置时再补apps/movies/app.dev.yaml - 写
apps/movies/src/panels/movies.tsx实现 UI 逻辑;需要时再补apps/movies/src/libs/**或*.module.css - 保存文件
几秒后 预览 面板会自动编译、自动挂载你的组件。
预览模式
预览面板顶部提供实时编译和预览操作:
| 操作 | 效果 |
|---|---|
| 重新编译 | 手动重新编译当前 App 源码,刷新预览结果 |
| 暂停实时编译 | 临时停止源码变更后的自动编译,适合连续修改时使用 |
| 整体预览 | 完全切换为你的 App 布局,自动打开第一个面板 |

预览模式下右下角有 「返回开发」 悬浮球,双击 ESC 也能退出。即使 AI 写出了死循环代码,逃生球一样能点。点击「返回开发」后会自动回到预览面板。

迭代优化
看到预览后,可以继续跟 AI 说:
把电影卡片的背景改成渐变色
加一个「已看过」的筛选条件
把评分数字字体放大一点
AI 会 Read → Edit 现有文件。由于 AppPreview 监听了源码变化,保存后自动重新编译刷新。
样式
默认有两档方式:
- 基础模式:直接在 TSX 里写
className,配合 Tailwind utility class。大多数场景够用,也是兼容性最稳的方式 - 增强模式:在当前环境已启用时,使用
*.module.css做局部样式抽离,并可在 App 根容器上定义局部 CSS 变量
增强模式的边界:
- 只支持
apps/<slug>/src/panels/**、apps/<slug>/src/libs/**下的*.module.css - 不支持普通
.css - 不支持
:global、html、body、:root、.dark - App 根容器变量只保证作用于当前 App 的非 Portal 子树
发布
满意后点 预览 工具栏的 「发布」 按钮。会弹出确认对话框,点击确认后:
- 指令会自动发到对话框(你不需要手动打字)
- AI 执行
app-developerSkill 的pack-and-upload.sh脚本 - 脚本用
build-panels.mjs编译 TSX、校验 import / CSS 规则 → 打 ZIP → 直接调POST /api/v1/apps - 返回新 App ID
完成后:
- 此 App 出现在 能力中心 → 应用 → 自建应用 Tab 里
- 有权限的成员可以在空间设置里把工作空间切换到此 App
外部开发 + 手动上传
如果你有现成的应用包 ZIP(含 app.yaml + panels/*.js,必要时带 sibling panels/*.css),在 能力中心 → 应用 → 自建应用 → 上传组织应用 上传即可。上传后的组织应用默认已对当前组织可用,不需要经过应用市场安装。
参考示例:apps/app-plugins/customer-management/(源码结构 + 打包脚本)。
可见性与管理
可见性管理
| 可见性 | 谁能看到 | 典型用途 |
|---|---|---|
| 私有(PRIVATE) | 仅创建者 | 自用的个人工具、开发中未稳定的版本 |
| 组织内(INTERNAL) | 同组织成员 | 团队内部复用的标准应用 |
| 公开(PUBLIC) | 所有组织 | 平台级模板、合作伙伴开放的公共应用 |
上传后,当前组织内的组织管理员或应用创建者可在 能力中心 → 应用 → 某个 App 详情 → 编辑 中修改可见性。来自其他组织的公开应用不会展示编辑和重新上传入口。
组织安装与内置应用
- 自建应用:属于当前组织,上传或发布后自动可用,主要通过「自建应用」Tab 管理。
- 应用市场应用:来自公开市场,组织管理员需要先在「应用市场」Tab 点击「安装」,安装后本组织工作空间才可选择和绑定。市场中只展示当前组织的安装状态,不展示该应用被多少其他组织安装。
- 平台内置应用:例如「通用」和「应用模板开发」,展示在「应用市场」Tab 中,但标记为「默认可用」,不需要安装,也不能卸载。
覆盖发布(迭代已有 App)
同一个 slug 的 App 再次发布时,系统会判断:
- 你是原创建者 → 弹出确认对话框,提示会覆盖当前版本,所有绑定此 App 的工作空间会立即使用新版。你点「覆盖」后完成更新
- 你不是原创建者 → 拒绝覆盖,必须修改
slug后重新发布(或联系原作者)
⚠️ 覆盖发布会立即影响所有绑定此 App 的工作空间 —— 如果跨团队共用,建议版本号同步升级,并提前告知相关成员
使用应用模板开发空间修改后再发布时,如果是同一个 slug,AI 会先发一次请求 → 后端返回 409 冲突 → AI 问你是否覆盖。你确认后 AI 再发一次带覆盖标识的请求,成功后告诉你「已覆盖更新」。
权限保护:只有原作者能覆盖。如果你在别人的空间里发布一个同 slug 的 App,会被后端拒绝(403),需要你改 slug 重试。
删除 App
在 能力中心 → 应用 → 自建应用 删除后:
- 如果当前没有空间使用此 App → 直接删除
- 如果有空间使用此 App → 弹出确认对话框,列出受影响的空间。确认后:
- App 被删除
- 这些空间的
appId自动降级为「通用」App - 空间内容完全保留(只是失去了自定义面板,回到默认布局)
面板可用的平台能力
除了纯 React 组件能力,面板还可以调用平台暴露的三类能力做更复杂的事。
1. @knodo/api — 调用平台前端行为
用在「面板想让 AI 帮我做事」这类场景。这些能力不是后端 API(没有 HTTP endpoint),必须走 @knodo/api。
| 方法 | 作用 |
|---|---|
chat.send(text, options?) | 把一段文本直接作为用户消息发送到当前或指定对话 Tab,AI 立即开始回复 |
chat.fill(text, options?) | 把文本填进当前或指定对话 Tab 的输入框,留给用户修改和确认 |
chat.open() | 打开工作空间右侧对话面板 |
chat.close() | 关闭工作空间右侧对话面板 |
chat.getPanelState() | 读取右侧对话面板当前是否可见、是否可打开、当前布局模式 |
chat.subscribePanelState(listener) | 订阅右侧对话面板状态变化,返回取消订阅函数 |
app.preventUnload.enable(options?) | 开启离开保护,适合长任务运行中使用 |
app.preventUnload.disable() | 关闭离开保护,任务完成、失败或取消后调用 |
app.preventUnload.isEnabled() | 查询当前是否已开启离开保护 |
示例:
import { app, chat } from "@knodo/api";
<button onClick={() => chat.send("帮我分析这条订单异常")}>交给 AI 分析</button>;
<button onClick={() => chat.open()}>打开对话</button>;
<button onClick={() => chat.close()}>关闭对话</button>;这些方法同步触发且没有返回值。send / fill 不会自动改变对话面板的开关状态;需要展示右侧对话区时,先调用 chat.open()。open / close 只控制可见性,不会创建、切换或关闭对话 Tab,也不会清空会话状态。
自定义面板需要展示打开/收起图标时,通过状态 API 判断当前状态:
import { useEffect, useState } from "react";
import { chat } from "@knodo/api";
function ChatPanelToggle() {
const [state, setState] = useState(() => chat.getPanelState());
useEffect(() => chat.subscribePanelState(setState), []);
return (
<button
disabled={!state.canOpen}
onClick={() => (state.open ? chat.close() : chat.open())}
>
{state.open ? "收起对话" : "打开对话"}
</button>
);
}chat.getPanelState() 返回 { open, canOpen, mode, reason }。open 表示对话区当前是否对用户可见,canOpen=false 表示宿主不允许打开,reason 可能是 manifest-hidden 或 fullscreen-preview。chat.subscribePanelState(listener) 会在订阅时立即回调一次当前状态,并在后续状态变化时继续通知。
在移动端,应用预览和对话区不是并排布局:chat.open() 会关闭覆盖在对话区上的应用预览抽屉,chat.close() 不会隐藏移动端主对话区。如果 App manifest 已通过 layout.sidebar.hide 隐藏 chat,面板不能用 chat.open() 绕过该配置。
长任务的离开保护是运行时能力,不写在 app.yaml。面板在任务开始时调用 app.preventUnload.enable(),任务完成、失败或取消后调用 app.preventUnload.disable()。开启后会保护浏览器刷新、关闭标签页/浏览器,以及切换空间详情菜单、切到其他 App 面板、关闭移动端 App 预览层等应用内离开动作。浏览器刷新/关闭的确认文案由浏览器控制;应用内离开确认可通过 message 自定义。
import { app } from "@knodo/api";
async function runLongTask() {
app.preventUnload.enable({ message: "任务还在运行,确定离开吗?" });
try {
await doLongTask();
} finally {
app.preventUnload.disable();
}
}2. @knodo/ui — 复用平台组件
有些组件实现极复杂,自己写不划算。平台把它们以 @knodo/ui 形式对面板开放。
| 当前可用 | 说明 |
|---|---|
ChatInterface | 完整的 AI 对话窗口。包含消息列表、流式渲染、命令选择、文件上传、模型切换等。面板可以直接嵌入用 |
示例(把对话嵌进自己的面板):
import { ChatInterface } from "@knodo/ui";
export default function MyPanel({ workspaceId }) {
return (
<div className="flex h-full flex-col">
<header>...我的业务内容...</header>
<div className="flex-1">
<ChatInterface
workspaceId={workspaceId}
welcomeMessage="💡 可以问我任何关于这份数据的问题"
hideFileSelector
/>
</div>
</div>
);
}
@knodo/ui会随着平台迭代逐步增加新组件(看板、Markdown 查看器等)。
3. 直接 fetch 后端 API
面板运行在 same-origin,所有 /api/v1/... 接口都能用标准 fetch 调用,凭证自动携带:
fetch(`/api/v1/workspaces/${workspaceId}/tasks`, {
credentials: "include",
}).then((r) => r.json());注意事项
- 面板跑在平台主 React 树里,不是 iframe。基础主题 token 会在非 Portal 子树中自动继承;高级场景可在启用后使用
*.module.css和 App 根容器 CSS 变量 @knodo/api和@knodo/ui的类型提示需要编辑器支持,AI 直接写就行- 所有能力都有版本承诺:
@knodo/api和@knodo/ui的破坏性变更会通过 Skill 文档和发版说明告知 - 不要尝试绕过去 import
@/...,会编译失败 - 不要指望 App 根容器变量自动影响
Dialog、Popover、Tooltip等 Portal 浮层
对接自定义后端服务
App 模板可以在选中的 App 根目录里的 app.yaml 中声明自定义后端端点,让面板通过 Knodo 的 App API Proxy 调用外部服务。面板只调用平台注入的 apiBaseUrl,不要手写平台代理 URL。
api:
endpoints:
- path: /customers
methods: [GET, POST]
targetUrl: https://crm.example.com/customersinterface PanelProps {
workspaceId: string;
appSlug?: string;
apiBaseUrl?: string;
}
export default function CustomersPanel({ apiBaseUrl }: PanelProps) {
async function loadCustomers() {
if (!apiBaseUrl) return;
const response = await fetch(`${apiBaseUrl}/customers`, {
credentials: "include",
});
return response.json();
}
return <button onClick={loadCustomers}>加载客户</button>;
}代理会剥离浏览器传入的 Cookie、raw Authorization 和伪造的 Javis-* 头,只向自定义后端注入服务端生成的可信上下文:
| Header | 说明 |
|---|---|
Javis-Login-User-Email | 当前登录用户邮箱 |
Javis-Login-User-Id | 当前登录用户 ID |
Javis-Organization-Id | 当前工作空间所属组织 |
Javis-Workspace-Id | 当前工作空间 ID |
如果扩展后端还需要 elevoOrgId / elevoUserId,不要继续扩展 App Proxy 透传头;改为调用 GET /api/v1/organizations/{organizationId}/member-access/elevo-identity?userId=...,把 Knodo 的组织 ID 和用户 ID 映射到 elevoOne 身份。这个接口属于 App Token 管理类,不需要用户级 JWT。
开发预览态会读取当前工作空间选中的 App 根目录里的 app.dev.yaml(优先)或 app.yaml 草稿,不要求 App 已发布或绑定;发布运行态要求 App 已发布并绑定到当前工作空间。
如果自定义后端还需要反向调用 Knodo API,可以使用 M2M API 密钥获取受控 Token。对于代表具体用户的调用,后端先获取 App Token,再通过 Token Exchange 换取单组织 User JWT;如果只需要查 App Token 管理类接口,则直接使用 App Token 即可。详细凭证创建、App Token 管理类接口和用户身份 Token Exchange 流程见平台级应用:认证对接。
技术规范
文件约定
AI 生成的文件结构(你能在「代码」面板里看到):
<workspace-root>/
├── apps/
│ └── <slug>/
│ ├── app.dev.yaml # 开发预览配置(可选)
│ ├── app.yaml # App Manifest(必需)
│ ├── package.json # npm 依赖声明(可选)
│ ├── src/
│ │ ├── panels/
│ │ │ ├── <view-key-1>.tsx
│ │ │ ├── <view-key-1>.module.css
│ │ │ └── <view-key-2>.tsx
│ │ └── libs/ # 共享组件 / hooks / utils(可选)
│ └── dist/ # 打包产物(脚本生成,无需手动编辑)
│ └── <slug>.zip
├── docs/ # 共用文档(不打包)
└── ...严格命名约定
view key ⇄ entry 文件名 ⇄ 源码文件名 必须三者对齐:
app.yaml的views.<key>.entry = ./panels/<key>.js(发布后的路径)- 源码
apps/<slug>/src/panels/<key>.tsx - 如果启用了 CSS Modules,构建后可能还会生成
./panels/<key>.css作为样式 sidecar;这是产物文件,不是源码入口 - 已发布 App 会尝试加载同名 CSS sidecar;旧 App 没有
./panels/<key>.css时继续只加载 JS,不会白屏
违反约定会导致预览报"源文件不存在"。AI 会严格遵守这个约定。
app.yaml 约束
name: 客户管理
slug: crm
version: 1.0.0
description: CRM 客户关系管理
category: CRM
views:
customers:
title: 客户列表
type: component
entry: ./panels/customers.js
api:
endpoints:
- path: /customers
methods: [GET, POST]
targetUrl: https://crm.example.com/customers
layout:
sidebar:
hide: [tasks, changes] # 隐藏的内置面板
rename:
files: 资料 # 把"知识"重命名为"资料"
panels:
- key: customers
title: 客户列表
icon: Users| 字段 | 说明 |
|---|---|
name | 必填 |
slug | 必填,小写字母 + 数字 + 连字符 |
version | 必填,x.y.z 格式 |
views | 必填,至少一个面板定义 |
api.endpoints | 可选,自定义后端代理端点列表,详见对接自定义后端服务 |
layout.sidebar.hide | 可选,要隐藏的内置面板 ID 数组 |
layout.sidebar.rename | 可选,内置面板重命名映射(如 { files: 代码 }) |
api.endpoints 中每一项包含:
| 字段 | 说明 |
|---|---|
path | 面板调用的代理路径,必须以 / 开头 |
methods | 允许转发的 HTTP 方法,如 [GET, POST] |
targetUrl | 自定义后端目标地址,必须是 http 或 https URL |
发布后,这份 manifest + 编译好的面板资产(panels/<key>.js,必要时带 panels/<key>.css sibling CSS)会被平台注册到应用模板库,供组织内/外成员复用。运行时会尝试加载同名 CSS sidecar;旧 App 没有 CSS 文件时仍按原有 JS 面板加载。
隐藏 / 重命名内置面板
默认平台空间有 7 个内置面板:
| ID | 默认文案 | 说明 |
|---|---|---|
tasks | 任务 | 任务管理 |
files / knowledge | 知识 | 知识库文件树(两个 ID 互为别名) |
changes | 变更 | Git 变更视图 |
views | 分析 | 数据视图 |
automation | 自动化 | 定时任务 |
guideline | 指引 | 工作指引 |
chat | —— | 右侧对话区 |
你可以告诉 AI:「不要任务和变更面板」→ AI 会在 manifest 加 hide: [tasks, changes]。也可以重命名:「把知识改名为资料」→ AI 加 rename: { files: 资料 }。完全沉浸式应用(只留一个面板)也可以:「只保留我的面板,其他全部隐藏,包括对话区」。

TSX 文件约束
AI 生成的面板组件需要满足:
export default导出 React 组件- 组件接收平台注入的运行时 props:
workspaceId:当前工作空间 ID,必定存在appId:当前 App ID,预览未发布时可能为空appSlug:当前 App slugappName:当前 App 名称apiBaseUrl:当前 App 在当前工作空间下的 API Proxy 基础路径;开发预览态会读取当前选中的 App 根目录里的app.dev.yaml(优先)或app.yaml草稿,发布运行态会读取已发布并绑定的 App 配置
- 可以 import
react(Hook 都能用) - 可以 import 下列平台能力(详见面板可用的平台能力):
@knodo/api:发送或预填对话消息,以及打开或关闭右侧对话面板@knodo/ui:复用平台桥接组件(目前稳定开放:ChatInterface)- 可以直接
fetch任何/api/v1/...后端接口(same-origin 自动带凭证) - 不能 import 平台私有模块(
@/components/...、@/stores/...等)
- Tailwind 类名可用
- 在当前环境已启用时,可以 import
*.module.css并与className混用 - 普通
.css不可用;也不要写:global、html、body、:root、.dark
示例:
interface PanelProps {
workspaceId: string;
appId?: string;
appSlug?: string;
appName?: string;
apiBaseUrl?: string;
}
export default function CustomersPanel({ apiBaseUrl }: PanelProps) {
if (!apiBaseUrl) {
return <div>当前 App 未提供 API Proxy 地址</div>;
}
// API Proxy 基础路径由平台注入,面板无需手动拼接 appSlug。
fetch(`${apiBaseUrl}/customers`, { credentials: "include" });
return <div>客户列表</div>;
}开发预览态和发布运行态的代理入口不同,但面板不需要感知差异:
- 开发预览态:平台注入
/api/v1/app-preview/workspaces/{workspaceId}/apps/{appRootId}/proxy - 发布运行态:平台注入
/api/v1/apps/{appSlug}/proxy/workspaces/{workspaceId}
开发预览态不要求 App 已发布或绑定到当前工作空间;发布运行态仍要求 App 已发布并绑定。
适用场景
适合做工作空间内嵌业务工具:
- 数据看板(内嵌 echarts / recharts 后可视化)
- 清单工具(待办、笔记、收藏)
- 表单类应用
- 轻量 CRM / 跟进工具
- 文档展示 / 知识库门户
- 通过 App API Proxy 对接自定义后端服务的业务面板
目前不适合做的:
- 需要重度 UI 组件库的应用(暂不共享 shadcn/ui,但可以用 Tailwind + 原生 HTML)
- 强依赖全局 CSS 覆盖或复杂 Portal 主题联动的应用(当前只保证局部 CSS Modules 和非 Portal 子树变量)
这些会在后续迭代中放开。
常见问题
Q: 预览报"未找到面板组件"?
A: 大概率是 view key 和源码文件名不对齐。让 AI 检查 app.yaml 里 views.<key>.entry 是否等于 ./panels/<key>.js,对应源码是否在 apps/<slug>/src/panels/<key>.tsx。
Q: 预览报"编译失败"?
A: AppPreview 顶部会显示编译错误,点击具体 view 的「失败」badge 看详细错误行。把错误原文发给 AI,让 AI 修。
Q: 面板里能发消息给对话区吗?
A: 能。import { chat } from '@knodo/api'; chat.send('你的消息') 就会把消息发到当前激活的对话 Tab。如果你只想预填入输入框让用户确认,用 chat.fill('...')。
Q: 面板里能打开或关闭右侧对话区吗?
A: 能。调用 chat.open() 打开,调用 chat.close() 关闭。它们只改变右侧对话面板的可见性,不会关闭 Conversation Tab 或清空对话状态。需要显示打开/收起图标时,用 chat.getPanelState() 读取当前状态,或用 chat.subscribePanelState(listener) 持续同步状态。移动端调用 chat.open() 会退出应用预览并回到对话区,chat.close() 不隐藏移动端主对话区;manifest 已隐藏 chat 时不能强制打开。
Q: 面板执行长任务时能防止误关闭或误切换吗?
A: 能。任务开始时调用 app.preventUnload.enable(),任务完成、失败或取消后调用 app.preventUnload.disable()。不要写成 app.yaml 的固定面板配置,否则面板空闲时也会打扰用户。
Q: 面板里能调用平台后端 API 吗?
A: 可以,直接 fetch('/api/v1/...', { credentials: 'include' }),凭证会自动带,后端按当前登录用户鉴权。
Q: 发布失败 409 但我是作者?
A: 重启 backend 让 DTO 字段生效(overwrite 字段是新加的)。正常情况下会弹确认对话框让你点「覆盖」。
Q: 预览模式下页面崩了,怎么回去?
A: 右下角逃生悬浮球永远可点,或者双击 ESC。
Q: 发布的 App 别人看不到?
A: 检查可见性:默认是 PRIVATE(只有你自己)。在能力中心的应用详情里编辑可见性为 INTERNAL(组织内)或 PUBLIC(跨组织)。公开 App 可被其他组织看到并安装,但其他组织不会看到编辑、重新上传 ZIP 等归属组织管理入口。
相关文档
- 独立应用认证对接 - M2M API 密钥接入指南
- 站点发布 - 独立站点发布与授权
- API Key 使用指南(模型调用) - AI 模型渠道 API Key
- 源码位置:
apps/plugins/javis/skills/app-developer/(Skill 定义)