https://github.com/tangle-network/sandbox-ui
React component library for AI agent interfaces — chat, terminal, file browser, tool calls, dashboards
https://github.com/tangle-network/sandbox-ui
ai-agents components radix-ui react sandbox tailwindcss tangle typescript ui
Last synced: 19 days ago
JSON representation
React component library for AI agent interfaces — chat, terminal, file browser, tool calls, dashboards
- Host: GitHub
- URL: https://github.com/tangle-network/sandbox-ui
- Owner: tangle-network
- Created: 2026-03-18T20:48:25.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-04-02T05:33:39.000Z (25 days ago)
- Last Synced: 2026-04-02T05:36:30.139Z (25 days ago)
- Topics: ai-agents, components, radix-ui, react, sandbox, tailwindcss, tangle, typescript, ui
- Language: TypeScript
- Size: 731 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README

# @tangle-network/sandbox-ui
React component library for [Tangle Sandbox](https://sandbox.tangle.tools) — a shadcn-style primitive layer plus higher-order sandbox surfaces for agent chat, files, runtime state, artifacts, and dashboard views.
## Install
```bash
npm install @tangle-network/sandbox-ui
```
**Peer dependencies:** `react` and `react-dom` (18 or 19). Optional peers for specific subpaths — see [package.json](./package.json).
## Usage
```tsx
import {
SandboxWorkbench,
type FileNode,
type SessionMessage,
type SessionPart,
} from "@tangle-network/sandbox-ui";
```
Import styles in your app root:
```tsx
import "@tangle-network/sandbox-ui/styles";
```
If you are building on the sandbox SDK directly, use `useSdkSession` to turn raw SDK/session-gateway events into the `messages + partMap` model that `ChatContainer` and `SandboxWorkbench` expect:
```tsx
import {
SandboxWorkbench,
} from "@tangle-network/sandbox-ui";
import { useSdkSession } from "@tangle-network/sandbox-ui/sdk-hooks";
function App() {
const {
messages,
partMap,
isStreaming,
appendUserMessage,
beginAssistantMessage,
applySdkEvent,
completeAssistantMessage,
failAssistantMessage,
} = useSdkSession();
async function runTurn(text: string) {
appendUserMessage({ content: text });
const assistantMessageId = beginAssistantMessage();
try {
for await (const event of sdk.streamPrompt(text)) {
applySdkEvent(event, { messageId: assistantMessageId });
}
completeAssistantMessage({ messageId: assistantMessageId });
} catch (error) {
failAssistantMessage(
error instanceof Error ? error.message : "Agent run failed",
{ messageId: assistantMessageId },
);
}
}
return (
);
}
```
Compose sandbox applications around `SandboxWorkbench` when you want the library’s default operating model:
```tsx
const root: FileNode = {
name: "agent",
path: "/home/agent",
type: "directory",
children: [],
};
const messages: SessionMessage[] = [];
const partMap: Record = {};
;
```
`FileTreeVisibilityOptions` is a UI-layer policy only. Sensitive paths still need to be hidden and denied by the app/backend layer.
## Theming And Retheming
There is a built-in Tangle default theme, but consumers can restyle the library in three layers:
1. Pick a built-in surface theme
2. Override semantic tokens
3. Wrap higher-level components when you want a different product composition
### 1. Pick a Built-in Theme
`WorkspaceLayout` and `SandboxWorkbench` support:
- `theme="vault"` — light theme with solid surfaces
- No theme prop — default dark theme
They also support `density="comfortable"` and `density="compact"`.
```tsx
```
If you are not using `SandboxWorkbench`, you can set the same attributes yourself:
```tsx
```
### 2. Override Semantic Tokens
The shared visual contract lives in [src/styles/tokens.css](./src/styles/tokens.css). The important tokens are:
- surfaces: `--bg-root`, `--bg-card`, `--bg-elevated`, `--bg-section`, `--bg-input`
- text: `--text-primary`, `--text-secondary`, `--text-muted`
- brand: `--brand-cool`, `--brand-glow`, `--brand-purple`
- accent surfaces: `--accent-gradient-strong`, `--accent-surface-soft`, `--accent-surface-strong`, `--accent-text`
- borders: `--border-subtle`, `--border-default`, `--border-accent`
- radii/shadows: `--radius-*`, `--shadow-card`, `--shadow-dropdown`, `--shadow-accent`
App-level overrides can be scoped to a wrapper:
```css
.tax-theme {
--brand-cool: hsl(187 75% 54%);
--brand-glow: hsl(164 74% 56%);
--bg-root: hsl(222 18% 9%);
--bg-card: hsl(223 20% 12%);
--border-accent: hsl(187 75% 48% / 0.35);
--font-sans: "Satoshi", ui-sans-serif, system-ui, sans-serif;
}
```
```tsx
```
### 3. Know When To Wrap Instead Of Override
Token overrides are the right tool when you want:
- a different brand color system
- different typography
- tighter or roomier density
- a more consumer-facing or operator-facing tone
Wrap or compose on lower-level exports when you want:
- a different page shell
- different header chrome
- a different artifact tab model
- app-specific empty states and actions
The higher-order dashboard/billing surfaces are now accent-token driven rather than hardcoded to the default Tangle look. The main seams are:
- `DashboardLayout.className`, `sidebarClassName`, `contentClassName`
- `BillingDashboard.className`, `cardClassName`
- `PricingCards.className`, `cardClassName`
- `UsageChart.className`
- `StandalonePricingPage.className`
For that, compose directly from:
- `/workspace`
- `/chat`
- `/run`
- `/files`
### Current Reality
Retheming is absolutely supported, but the documentation was thinner than it should be. The token layer is strong; the higher-level surfaces are themeable, but more opinionated. For a radically different product look, prefer keeping the token contract and wrapping the higher-level workbench/chat surfaces rather than fighting every internal class.
## Docs
| Guide | Description |
|-------|-------------|
| [Sidebar](./docs/sidebar.md) | Composable Rail + Panel sidebar system (architecture, components, full API) |
## Subpath Exports
| Subpath | Description |
|---------|-------------|
| `/primitives` | Button, Card, Dialog, Badge, Input, Select, Table, Tabs, Toast, etc. |
| `/chat` | ChatContainer, ChatInput, ChatMessage, AgentTimeline, ThinkingIndicator |
| `/run` | ToolCallFeed, RunGroup, InlineToolItem, ExpandedToolDetail |
| `/workspace` | SandboxWorkbench, WorkspaceLayout, DirectoryPane, RuntimePane, StatusBar |
| `/openui` | OpenUIArtifactRenderer and schema types for structured artifact rendering |
| `/files` | FileTree, FilePreview, FileTabs, FileArtifactPane |
| `/dashboard` | [Sidebar](./docs/sidebar.md), DashboardLayout, BillingDashboard, UsageChart, ProfileSelector |
| `/editor` | TipTap collaborative editor (requires optional peers) |
| `/terminal` | xterm.js terminal view (requires optional peers) |
| `/markdown` | Markdown renderer with GFM, code blocks, copy button |
| `/auth` | AuthHeader, GitHubLoginButton, UserMenu |
| `/pages` | Pre-built billing, pricing, profiles pages |
| `/hooks` | useSSEStream, useAuth, usePtySession, useRunGroups, etc. |
| `/sdk-hooks` | Lightweight session/stream hooks without the React Query CRUD hook bundle |
| `/stores` | Session and chat nanostores |
| `/types` | TypeScript types for messages, parts, runs, sessions |
| `/utils` | cn, formatDuration, timeAgo, tool display helpers |
| `/styles` | Compiled CSS bundle |
## Stack
- [Radix UI](https://www.radix-ui.com/) primitives
- [Tailwind CSS](https://tailwindcss.com/) v4
- [Lucide](https://lucide.dev/) icons
- [CVA](https://cva.style/) for variant management
- Shared semantic tokens for default dark and `vault` light sandbox themes
- ESM-only, tree-shakeable, fully typed
## License
Apache-2.0