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

# Page Client API

> The host injects window.mcode into the Mini App's main page through preload. Check that a method exists before calling it.

<div className="code-docs">
  No npm SDK, session ID, or plugin ID is needed. Older hosts, ordinary browser tabs, and external browsers don't have this object, so always feature-detect. You can copy `client-api.ts` from the Skill into your project for type checking; don't load it at runtime.

  <h2 id="detect">
    Feature detection
  </h2>

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

  if (!miniapp?.message?.append) {
    showHint('This host cannot hand content to the message input.');
  }
  if (miniapp?.window) {
    // Pop-out window support is available
  }
  if (miniapp?.agent) {
    // The Agent API is available and the manifest declares agentAccess
  }
  ```

  <h2 id="append">
    message.append(text)
  </h2>

  Call it from a button the user clicked. The host shows its own confirmation dialog with the plugin, the target conversation, and the text. After confirmation, the text is appended to that conversation's message input. The user can edit it and must press Send to start a turn.

  ```javascript theme={null}
  async function handToAgent(text, button, status) {
    const message = window.mcode?.miniapp?.message;
    if (!message?.append) {
      status.textContent = 'This host cannot add text to the message input.';
      return;
    }
    button.disabled = true;
    try {
      await message.append(text);
      status.textContent = 'Added to the message input. Review it there and press Send.';
    } catch (error) {
      const code = error?.message;
      if (code === 'user_cancelled') status.textContent = 'Cancelled. Nothing was added.';
      else if (code === 'result_unknown') status.textContent = 'Check the message input first; the text may already be there.';
      else status.textContent = 'Could not add the text. Check the conversation and try again.';
    } finally {
      button.disabled = false;
    }
  }
  ```

  * `text` is plain text, up to 100,000 UTF-16 code units per call. HTML is treated as text; add newlines yourself if you need them.
  * Every call needs confirmation. There is no remembered choice and no automatic sending. Multiple calls are confirmed one at a time, in order.
  * Existing draft text, references, and attachments are preserved. A Mini App can't read chat history or change sent messages.
  * On failure, `message` is `user_cancelled`, `invalid_request`, `text_too_large`, `not_allowed`, `target_unavailable`, or `result_unknown`. For `result_unknown`, ask the user to check the message input first; never repeat the append automatically.

  Make the destination clear on the button, for example "Ask Agent". A resolved promise only means the text reached the input, not that the Agent has started working.

  <h2 id="window">
    miniapp.window
  </h2>

  <Badge color="orange" size="sm">Experimental</Badge>

  A handle to the Mini App's own window: whether the page sits in the file panel (`inline`) or is popped out (`window`), plus position, size, and a few window flags while popped out. The interface may still change, so always feature-detect first.

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

    // Changing mode must happen inside a user gesture, such as a synchronous click handler
    popOutButton.onclick = () => handle.update({ mode: 'window', width: 900, height: 650 });

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

  * Changing `mode` or calling `focus()` must come from a user gesture; otherwise it rejects with `not_permitted`.
  * `frame`, `transparent`, `minWidth`, and `minHeight` can only be declared in the manifest's `surface.window` and can't change at runtime.
  * `alwaysOnTop: true` and `clickThrough: true` can be turned on at runtime only if the manifest declares them as `true`.
  * Multiple `update` calls in the same frame are merged into one; a patch with `mode` is sent immediately so it keeps the user gesture.
  * Errors: `inline_mode`, `not_permitted`, `busy`, `result_unknown`, `invalid_request`.

  <h3 id="window-pitfalls">
    Pop-out window pitfalls
  </h3>

  | Setting | Watch out for |
  | - | - |
  | `frame: false` | Removes the OS title bar and its drag region; provide your own with CSS `-webkit-app-region: drag` |
  | `transparent: true` | Only removes the window's own background; the page `body` must be transparent too. The host turns off the native shadow for transparent windows, so draw one with CSS if you need it |
  | Window height | The window doesn't grow with content. Use `html, body { height: 100%; overflow: hidden }` and let the main area scroll |
  | `clickThrough: true` | Turn it on only for states that should be visible without catching clicks; otherwise nothing in the window, including its own buttons, is clickable |
  | `visible: false` | Only hides the window; scripts, timers, and network requests on the page keep running |

  <h2 id="agent">
    miniapp.agent
  </h2>

  Start Agent sessions from the page, render tool approvals and questionnaires yourself, or hand a session to the host sidebar with `open`. Requires `agentAccess` in the manifest; see [Agent API](/docs/miniapp/authors/agent#agent-api).

  <h2 id="page-rules">
    Page conventions
  </h2>

  * Use relative or same-origin URLs, with business APIs served by the Node service. Credentials, Connector clients, and host transport never enter the page.
  * Use semantic HTML: landmarks, headings, tables, forms, `role="status"`, and so on. That is how the Agent understands the page when it checks it in the browser.
  * Keep third-party scripts out of the main document. Code there, including a same-origin iframe that can reach its parent, shares the page's trust.
  * Don't load remote resources: use local files or inline SVG for fonts, icons, and images, never a remote CDN. Pages then work offline, and third-party scripts have less chance to reach local services under the page's identity.
  * Without a specific style request, follow the MiniMax Code visual baseline in the Skill.
</div>
