Clip Web UI
Clips can include a web UI that runs inside the Pinix Console. This lets users interact with your Clip through a visual interface in addition to CLI and Agent invocation.
Architecture
Section titled “Architecture”The Clip Web UI runs in an isolated context:
Console (pinixai.com / localhost:9000) └── iframe: /<alias>/web/ └── Your Clip's web/ directory └── Calls Clip commands via @pinixai/core/webThe Web UI communicates with the Clip through @pinixai/core/web, which connects to the Hub. The Console merely embeds and frames the Clip’s Web UI — it doesn’t own it.
Quick setup
Section titled “Quick setup”Create a web/ directory in your Clip project:
my-clip/├── clip.json├── src/│ └── index.ts└── web/ ├── index.html ├── src/ │ └── main.ts └── package.json # Optional: if using a build toolMinimal web/index.html
Section titled “Minimal web/index.html”<!DOCTYPE html><html><head> <meta charset="utf-8" /> <title>My Clip</title></head><body> <div id="app"></div> <script type="module"> import { createClient } from "@pinixai/core/web";
const client = createClient();
async function loadTasks() { const result = await client.invoke("list"); document.getElementById("app").innerHTML = result.items .map(item => `<div>${item.title} [${item.status}]</div>`) .join(""); }
loadTasks(); </script></body></html>Using a framework
Section titled “Using a framework”You can use React, Vue, Svelte, or any framework. Set up a standard Vite project in the web/ directory:
cd webbun create vite . --template react-tsbun add @pinixai/coreConfigure vite.config.ts for Clip Web:
import { defineConfig } from "vite";import react from "@vitejs/plugin-react";
export default defineConfig({ plugins: [react()], base: "./", // Important: relative paths for Hub serving build: { outDir: "dist", },});@pinixai/core/web API
Section titled “@pinixai/core/web API”import { createClient } from "@pinixai/core/web";
const client = createClient();
// Invoke a commandconst result = await client.invoke("list");const task = await client.invoke("get", { id: "t1" });const created = await client.invoke("add", { title: "New task" });
// Streaming invokeconst stream = client.invokeStream("monitor");for await (const event of stream) { console.log("Update:", event);}Trust model
Section titled “Trust model”The Clip Web UI operates in a three-layer trust model:
Browser (untrusted) │ @pinixai/core/web ▼Hub (routing + auth) │ IPC / ProviderStream ▼Clip Runtime (trusted execution)- The Web UI runs in the browser — it’s untrusted
- All invocations go through the Hub, which handles authentication
- The Clip’s handler runs server-side in the Runtime
This means: never put secrets in your Web UI code. API keys, tokens, and sensitive logic belong in your Clip handler, not in the browser.
Development workflow
Section titled “Development workflow”- Start your Clip locally:
pinix hub add local/my-clip --path . - Access the Web UI: Open
http://localhost:9000/clips/my-clip/webin the Console - Iterate: Edit
web/files, refresh the browser to see changes - Build for production:
cd web && bun run build— the built assets inweb/dist/are what gets published
Deployment notes
Section titled “Deployment notes”When you pinix registry publish, the web/ directory (or web/dist/ if built) is included in the package. The Hub serves it automatically when users access the Clip’s Web UI.
Common pitfalls:
- Cookie/SameSite issues: Cloud Hub serves Web UIs from
*.hub.pinix.aisubdomains. Cookies must useSameSite=None; Secure. - CORS: All invocations go through
@pinixai/core/webwhich handles CORS internally. Don’t make direct HTTP requests to the Hub. - MIME types: Ensure your build outputs correct file extensions. The Hub serves files based on extension.