> ## Documentation Index
> Fetch the complete documentation index at: https://agent.minimaxi.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 页面 Client API

> 宿主通过 preload 向 Mini App 主页面注入 window.mcode。调用之前先确认方法存在。

不需要安装 npm SDK，也不需要传会话 ID 或插件 ID。旧版宿主、普通浏览器标签和外部浏览器里都没有这个对象，所以每次都要做特性检测。Skill 提供的 `client-api.ts` 可以拷进项目做类型检查，不要在运行时引入它。

## 特性检测

```javascript theme={null}
const miniapp = window.mcode?.miniapp;

if (!miniapp?.message?.append) {
  showHint('当前宿主不支持把内容交给输入框');
}
if (miniapp?.window) {
  // 弹出窗口能力可用
}
if (miniapp?.agent) {
  // Agent API 可用，并且 manifest 声明了 agentAccess
}
```

## message.append(text)

从用户点击的按钮里调用。宿主会弹出自己的确认框，显示插件、目标会话和文字；用户确认后，文字追加到该会话的输入框。用户可以再编辑，并且必须自己按发送，才会开始一轮对话。

```javascript theme={null}
async function handToAgent(text, button, status) {
  const message = window.mcode?.miniapp?.message;
  if (!message?.append) {
    status.textContent = '当前宿主不支持把内容交给输入框。';
    return;
  }
  button.disabled = true;
  try {
    await message.append(text);
    status.textContent = '已放进输入框，确认后请自己按发送。';
  } catch (error) {
    const code = error?.message;
    if (code === 'user_cancelled') status.textContent = '已取消，没有添加任何内容。';
    else if (code === 'result_unknown') status.textContent = '先看一下输入框，内容可能已经加进去了。';
    else status.textContent = '没能添加，检查会话后再试。';
  } finally {
    button.disabled = false;
  }
}
```

* `text` 是纯文本，单次最多 100,000 个 UTF-16 码元。HTML 也按文字处理；需要换行就自己在文字里加。
* 每次调用都要确认，没有"记住选择"，也不会自动发送。多次调用按顺序逐个确认。
* 输入框里已有的文字、引用和附件都会保留。Mini App 不能读聊天记录，也不能改已发送的消息。
* 失败时 `message` 是 `user_cancelled`、`invalid_request`、`text_too_large`、`not_allowed`、`target_unavailable` 或 `result_unknown`。遇到 `result_unknown` 时，提醒用户先看一下输入框，不要自动重发。

按钮上写清楚去向，比如"交给 Agent"。Promise 成功只代表文字进了输入框，不代表 Agent 已经开始工作。

## miniapp.window

实验性

Mini App 自己窗口的句柄：页面在文件面板里（`inline`），还是弹出成了独立窗口（`window`），以及弹出时的位置、尺寸和几个窗口属性。接口形状还可能调整，务必先检测再用。

```javascript theme={null}
const handle = window.mcode?.miniapp?.window;
if (handle) {
  const state = await handle.getState(); // { mode: 'inline' } 或 { mode: 'window', window: {...} }

  // 切换 mode 必须发生在用户手势里，比如同步的点击处理函数
  popOutButton.onclick = () => handle.update({ mode: 'window', width: 900, height: 650 });

  const subscription = handle.onDidChangeState((next) => render(next.mode));
  addEventListener('pagehide', () => subscription.dispose());
}
```

* 切换 `mode` 或调用 `focus()` 必须来自用户手势，否则直接以 `not_permitted` 拒绝。
* `frame`、`transparent`、`minWidth`、`minHeight` 只能在 manifest 的 `surface.window` 里声明，运行时不能改。
* `alwaysOnTop: true` 和 `clickThrough: true` 只有 manifest 里声明为 `true`，运行时才能打开。
* 同一帧内的多次 `update` 会合并成一次；带 `mode` 的会立即发出，以免丢掉用户手势。
* 错误：`inline_mode`、`not_permitted`、`busy`、`result_unknown`、`invalid_request`。

### 弹出窗口的常见坑

| 设置 | 要注意 |
| - | - |
| `frame: false` | 去掉了系统标题栏和拖动区域，要用 CSS `-webkit-app-region: drag` 自己提供拖动区域 |
| `transparent: true` | 只去掉窗口自身的背景，页面的 `body` 背景也要设成透明。宿主已关闭透明窗口的系统阴影，需要阴影就用 CSS 画 |
| 窗口高度 | 窗口不随内容长高。用 `html, body { height: 100%; overflow: hidden }`，让正文区域自己滚动 |
| `clickThrough: true` | 只在需要"看得见但不拦点击"的状态下打开，否则整个窗口包括自己的按钮都点不了 |
| `visible: false` | 只是隐藏窗口，页面里的脚本、定时器和网络请求仍在运行 |

## miniapp.agent

页面直接发起 Agent 会话，自己渲染工具审批和问卷，或者用 `open` 把会话交给宿主侧栏。需要在 manifest 里声明 `agentAccess`，见 [Agent API](/docs/miniapp/authors/agent#agent-api)。

## 写页面的约定

* 请求用相对路径或同源 URL，业务接口由 Node 服务提供。凭证、Connector 客户端和宿主传输都不进页面。
* 用语义化 HTML：landmark、heading、table、form、`role="status"` 等。Agent 用浏览器验收时，靠这些读懂页面。
* 不要把第三方脚本放进主文档。主文档里的代码，包括能访问父页面的同源 iframe，和页面共享同一份信任。
* 不加载远程资源：字体、图标和图片用本地文件或内联 SVG，不用远程 CDN。这样离线也能用，也减少第三方脚本借页面身份访问本机服务的风险。
* 没有特别的风格要求时，视觉以 Skill 里的 MiniMax Code 视觉基线为准。
