@tangle-network/sandbox-ui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -0
- package/dist/auth.d.ts +57 -0
- package/dist/auth.js +14 -0
- package/dist/branding-DCi5VEik.d.ts +13 -0
- package/dist/button-BidTtuRS.d.ts +15 -0
- package/dist/chat.d.ts +121 -0
- package/dist/chat.js +25 -0
- package/dist/chunk-2UHPE5T7.js +201 -0
- package/dist/chunk-4EIWPJMJ.js +545 -0
- package/dist/chunk-6MQIDUPA.js +502 -0
- package/dist/chunk-B26TQ7SA.js +47 -0
- package/dist/chunk-E6FS7R4X.js +109 -0
- package/dist/chunk-GRYHFH5O.js +110 -0
- package/dist/chunk-HMND7JPA.js +868 -0
- package/dist/chunk-HRMUF35V.js +19 -0
- package/dist/chunk-HYEAX3DC.js +822 -0
- package/dist/chunk-KMXV7DDX.js +174 -0
- package/dist/chunk-KYY2X6LY.js +318 -0
- package/dist/chunk-L6ZDH5F4.js +334 -0
- package/dist/chunk-LTFK464G.js +103 -0
- package/dist/chunk-M34OA6PQ.js +233 -0
- package/dist/chunk-M6VLC32S.js +219 -0
- package/dist/chunk-MCGKDCOR.js +173 -0
- package/dist/chunk-NI2EI43H.js +294 -0
- package/dist/chunk-OU4TRNQZ.js +173 -0
- package/dist/chunk-QD4QE5P5.js +40 -0
- package/dist/chunk-QSQBDR3N.js +180 -0
- package/dist/chunk-RQHJBTEU.js +10 -0
- package/dist/chunk-U62G5TS7.js +472 -0
- package/dist/chunk-ZOL2TR5M.js +475 -0
- package/dist/dashboard.d.ts +111 -0
- package/dist/dashboard.js +26 -0
- package/dist/editor.d.ts +196 -0
- package/dist/editor.js +713 -0
- package/dist/expanded-tool-detail-OkXGqTHe.d.ts +52 -0
- package/dist/files.d.ts +66 -0
- package/dist/files.js +11 -0
- package/dist/hooks.d.ts +22 -0
- package/dist/hooks.js +107 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +551 -0
- package/dist/markdown.d.ts +55 -0
- package/dist/markdown.js +17 -0
- package/dist/pages.d.ts +89 -0
- package/dist/pages.js +1181 -0
- package/dist/parts-CyGkM6Fp.d.ts +50 -0
- package/dist/primitives.d.ts +189 -0
- package/dist/primitives.js +161 -0
- package/dist/run-CtFZ6s-D.d.ts +41 -0
- package/dist/run.d.ts +14 -0
- package/dist/run.js +29 -0
- package/dist/sidecar-CFU2W9j1.d.ts +8 -0
- package/dist/stores.d.ts +28 -0
- package/dist/stores.js +49 -0
- package/dist/terminal.d.ts +44 -0
- package/dist/terminal.js +160 -0
- package/dist/tool-call-feed-D5Ume-Pt.d.ts +66 -0
- package/dist/tool-display-BvsVW_Ur.d.ts +32 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +0 -0
- package/dist/usage-chart-DINgSVL5.d.ts +60 -0
- package/dist/use-sidecar-auth-Bb0-w3lX.d.ts +339 -0
- package/dist/utils.d.ts +28 -0
- package/dist/utils.js +28 -0
- package/dist/workspace.d.ts +113 -0
- package/dist/workspace.js +15 -0
- package/package.json +174 -0
- package/src/styles/globals.css +230 -0
- package/src/styles/tokens.css +73 -0
- package/tailwind.config.cjs +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/tangle-network/sandbox-ui/main/.github/banner.svg" alt="Tangle Sandbox UI" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/@tangle-network/sandbox-ui"><img src="https://img.shields.io/npm/v/@tangle-network/sandbox-ui?color=8E59FF&label=npm" alt="npm" /></a>
|
|
7
|
+
<a href="https://github.com/tangle-network/sandbox-ui/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@tangle-network/sandbox-ui?color=6888F9" alt="license" /></a>
|
|
8
|
+
<a href="https://github.com/tangle-network/sandbox-ui"><img src="https://img.shields.io/github/stars/tangle-network/sandbox-ui?style=flat&color=8E59FF" alt="stars" /></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
# @tangle-network/sandbox-ui
|
|
12
|
+
|
|
13
|
+
React component library for [Tangle Sandbox](https://sandbox.tangle.tools) — 100+ components for building AI agent interfaces with chat, terminal, file browser, tool call feeds, and dashboard views.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @tangle-network/sandbox-ui
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Peer dependencies:** `react` and `react-dom` (18 or 19). Optional peers for specific subpaths — see [package.json](./package.json).
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Button, Badge, Card } from "@tangle-network/sandbox-ui/primitives";
|
|
27
|
+
import { ChatContainer } from "@tangle-network/sandbox-ui/chat";
|
|
28
|
+
import { WorkspaceLayout } from "@tangle-network/sandbox-ui/workspace";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Import styles in your app root:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import "@tangle-network/sandbox-ui/styles";
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Subpath Exports
|
|
38
|
+
|
|
39
|
+
| Subpath | Description |
|
|
40
|
+
|---------|-------------|
|
|
41
|
+
| `/primitives` | Button, Card, Dialog, Badge, Input, Select, Table, Tabs, Toast, etc. |
|
|
42
|
+
| `/chat` | ChatContainer, ChatInput, ChatMessage, MessageList |
|
|
43
|
+
| `/run` | ToolCallFeed, RunGroup, InlineToolItem, ExpandedToolDetail |
|
|
44
|
+
| `/workspace` | WorkspaceLayout, StatusBar, StatusBanner, TerminalPanel |
|
|
45
|
+
| `/files` | FileTree, FilePreview, FileTabs |
|
|
46
|
+
| `/dashboard` | DashboardLayout, BillingDashboard, UsageChart, ProfileSelector |
|
|
47
|
+
| `/editor` | TipTap collaborative editor (requires optional peers) |
|
|
48
|
+
| `/terminal` | xterm.js terminal view (requires optional peers) |
|
|
49
|
+
| `/markdown` | Markdown renderer with GFM, code blocks, copy button |
|
|
50
|
+
| `/auth` | AuthHeader, GitHubLoginButton, UserMenu |
|
|
51
|
+
| `/pages` | Pre-built billing, pricing, profiles pages |
|
|
52
|
+
| `/hooks` | useSSEStream, useAuth, usePtySession, useRunGroups, etc. |
|
|
53
|
+
| `/stores` | Session and chat nanostores |
|
|
54
|
+
| `/types` | TypeScript types for messages, parts, runs, sessions |
|
|
55
|
+
| `/utils` | cn, formatDuration, timeAgo, tool display helpers |
|
|
56
|
+
| `/styles` | Compiled CSS bundle |
|
|
57
|
+
|
|
58
|
+
## Stack
|
|
59
|
+
|
|
60
|
+
- [Radix UI](https://www.radix-ui.com/) primitives
|
|
61
|
+
- [Tailwind CSS](https://tailwindcss.com/) v4
|
|
62
|
+
- [Lucide](https://lucide.dev/) icons
|
|
63
|
+
- [CVA](https://cva.style/) for variant management
|
|
64
|
+
- ESM-only, tree-shakeable, fully typed
|
|
65
|
+
|
|
66
|
+
## License
|
|
67
|
+
|
|
68
|
+
Apache-2.0
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { a as ButtonProps } from './button-BidTtuRS.js';
|
|
4
|
+
import 'class-variance-authority/types';
|
|
5
|
+
import 'class-variance-authority';
|
|
6
|
+
|
|
7
|
+
interface SessionUser {
|
|
8
|
+
customer_id: string;
|
|
9
|
+
email: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
tier?: string;
|
|
12
|
+
github?: {
|
|
13
|
+
login: string;
|
|
14
|
+
connected: boolean;
|
|
15
|
+
} | null;
|
|
16
|
+
session_expires_at: string;
|
|
17
|
+
}
|
|
18
|
+
interface GitHubLoginButtonProps extends Omit<ButtonProps, "onClick"> {
|
|
19
|
+
/** API base URL (defaults to /auth/github) */
|
|
20
|
+
authUrl?: string;
|
|
21
|
+
/** Product variant for styling */
|
|
22
|
+
variant?: "sandbox" | "default" | "outline";
|
|
23
|
+
}
|
|
24
|
+
declare function GitHubLoginButton({ authUrl, variant, className, children, ...props }: GitHubLoginButtonProps): react_jsx_runtime.JSX.Element;
|
|
25
|
+
interface UserMenuProps {
|
|
26
|
+
user: SessionUser;
|
|
27
|
+
/** API base URL for logout */
|
|
28
|
+
logoutUrl?: string;
|
|
29
|
+
/** Links to show in menu */
|
|
30
|
+
links?: Array<{
|
|
31
|
+
href: string;
|
|
32
|
+
label: string;
|
|
33
|
+
icon?: React.ReactNode;
|
|
34
|
+
}>;
|
|
35
|
+
/** Product variant for styling */
|
|
36
|
+
variant?: "sandbox";
|
|
37
|
+
/** Callback when logout is clicked */
|
|
38
|
+
onLogout?: () => void;
|
|
39
|
+
}
|
|
40
|
+
declare function UserMenu({ user, logoutUrl, links, variant, onLogout, }: UserMenuProps): react_jsx_runtime.JSX.Element;
|
|
41
|
+
interface AuthHeaderProps {
|
|
42
|
+
/** Current session user (null if not logged in) */
|
|
43
|
+
user: SessionUser | null;
|
|
44
|
+
/** Whether session is loading */
|
|
45
|
+
loading?: boolean;
|
|
46
|
+
/** Product variant */
|
|
47
|
+
variant?: "sandbox";
|
|
48
|
+
/** API base URL */
|
|
49
|
+
apiBaseUrl?: string;
|
|
50
|
+
/** Links for user menu */
|
|
51
|
+
menuLinks?: UserMenuProps["links"];
|
|
52
|
+
/** Custom className */
|
|
53
|
+
className?: string;
|
|
54
|
+
}
|
|
55
|
+
declare function AuthHeader({ user, loading, variant, apiBaseUrl, menuLinks, className, }: AuthHeaderProps): react_jsx_runtime.JSX.Element;
|
|
56
|
+
|
|
57
|
+
export { AuthHeader, type AuthHeaderProps, GitHubLoginButton, type GitHubLoginButtonProps, type SessionUser, UserMenu, type UserMenuProps };
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthHeader,
|
|
3
|
+
GitHubLoginButton,
|
|
4
|
+
UserMenu
|
|
5
|
+
} from "./chunk-OU4TRNQZ.js";
|
|
6
|
+
import "./chunk-B26TQ7SA.js";
|
|
7
|
+
import "./chunk-MCGKDCOR.js";
|
|
8
|
+
import "./chunk-E6FS7R4X.js";
|
|
9
|
+
import "./chunk-RQHJBTEU.js";
|
|
10
|
+
export {
|
|
11
|
+
AuthHeader,
|
|
12
|
+
GitHubLoginButton,
|
|
13
|
+
UserMenu
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Visual branding for a run group header — injected by consuming app. */
|
|
2
|
+
interface AgentBranding {
|
|
3
|
+
label: string;
|
|
4
|
+
accentClass: string;
|
|
5
|
+
bgClass: string;
|
|
6
|
+
containerBgClass: string;
|
|
7
|
+
borderClass: string;
|
|
8
|
+
/** CSS class for the agent icon (legacy). Ignored when using lucide-react icons. */
|
|
9
|
+
iconClass: string;
|
|
10
|
+
textClass: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type { AgentBranding as A };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
2
|
+
import { VariantProps } from 'class-variance-authority';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
declare const buttonVariants: (props?: ({
|
|
6
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "sandbox" | null | undefined;
|
|
7
|
+
size?: "default" | "sm" | "lg" | "xl" | "icon" | null | undefined;
|
|
8
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
9
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
10
|
+
asChild?: boolean;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
}
|
|
13
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
14
|
+
|
|
15
|
+
export { Button as B, type ButtonProps as a, buttonVariants as b };
|
package/dist/chat.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import { S as SessionMessage, a as SessionPart } from './parts-CyGkM6Fp.js';
|
|
5
|
+
import { A as AgentBranding } from './branding-DCi5VEik.js';
|
|
6
|
+
import { C as CustomToolRenderer } from './tool-display-BvsVW_Ur.js';
|
|
7
|
+
import { G as GroupedMessage } from './run-CtFZ6s-D.js';
|
|
8
|
+
|
|
9
|
+
type MessageRole = "user" | "assistant" | "system";
|
|
10
|
+
interface ChatMessageProps {
|
|
11
|
+
role: MessageRole;
|
|
12
|
+
content: string;
|
|
13
|
+
/** Inline tool call activity rendered between text chunks */
|
|
14
|
+
toolCalls?: ReactNode;
|
|
15
|
+
/** Whether the message is still streaming */
|
|
16
|
+
isStreaming?: boolean;
|
|
17
|
+
/** Timestamp */
|
|
18
|
+
timestamp?: Date;
|
|
19
|
+
className?: string;
|
|
20
|
+
}
|
|
21
|
+
declare function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, }: ChatMessageProps): react_jsx_runtime.JSX.Element;
|
|
22
|
+
|
|
23
|
+
interface Message {
|
|
24
|
+
id: string;
|
|
25
|
+
role: MessageRole;
|
|
26
|
+
content: string;
|
|
27
|
+
toolCalls?: ReactNode;
|
|
28
|
+
isStreaming?: boolean;
|
|
29
|
+
timestamp?: Date;
|
|
30
|
+
}
|
|
31
|
+
interface ChatMessageListProps {
|
|
32
|
+
messages: Message[];
|
|
33
|
+
/** Render additional content after a message (e.g., tool call feed) */
|
|
34
|
+
renderAfter?: (message: Message, index: number) => ReactNode;
|
|
35
|
+
/** Content shown when no messages */
|
|
36
|
+
emptyState?: ReactNode;
|
|
37
|
+
/** Show thinking indicator at bottom */
|
|
38
|
+
isThinking?: boolean;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
declare function ChatMessageList({ messages, renderAfter, emptyState, isThinking, className, }: ChatMessageListProps): react_jsx_runtime.JSX.Element;
|
|
42
|
+
declare function ThinkingIndicator({ className }: {
|
|
43
|
+
className?: string;
|
|
44
|
+
}): react_jsx_runtime.JSX.Element;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* ChatInput — message input bar with file attach, send/cancel, model selector.
|
|
48
|
+
*
|
|
49
|
+
* - Auto-resizing textarea (up to max height)
|
|
50
|
+
* - Enter to send, Shift+Enter for newline
|
|
51
|
+
* - File attachment button with pending file chips
|
|
52
|
+
* - Cancel button when streaming
|
|
53
|
+
* - Optional model selector pill
|
|
54
|
+
*/
|
|
55
|
+
interface PendingFile {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
size: number;
|
|
59
|
+
status: "pending" | "uploading" | "ready" | "error";
|
|
60
|
+
}
|
|
61
|
+
interface ChatInputProps {
|
|
62
|
+
onSend: (message: string, files?: File[]) => void;
|
|
63
|
+
onCancel?: () => void;
|
|
64
|
+
isStreaming?: boolean;
|
|
65
|
+
disabled?: boolean;
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
/** Currently selected model label */
|
|
68
|
+
modelLabel?: string;
|
|
69
|
+
onModelClick?: () => void;
|
|
70
|
+
/** Pending uploaded files */
|
|
71
|
+
pendingFiles?: PendingFile[];
|
|
72
|
+
onRemoveFile?: (id: string) => void;
|
|
73
|
+
onAttach?: (files: FileList) => void;
|
|
74
|
+
className?: string;
|
|
75
|
+
}
|
|
76
|
+
declare function ChatInput({ onSend, onCancel, isStreaming, disabled, placeholder, modelLabel, onModelClick, pendingFiles, onRemoveFile, onAttach, className, }: ChatInputProps): react_jsx_runtime.JSX.Element;
|
|
77
|
+
|
|
78
|
+
interface ChatContainerProps {
|
|
79
|
+
messages: SessionMessage[];
|
|
80
|
+
partMap: Record<string, SessionPart[]>;
|
|
81
|
+
isStreaming: boolean;
|
|
82
|
+
onSend?: (text: string) => void;
|
|
83
|
+
branding?: AgentBranding;
|
|
84
|
+
placeholder?: string;
|
|
85
|
+
className?: string;
|
|
86
|
+
/** Hide the input area (useful for read-only views). */
|
|
87
|
+
hideInput?: boolean;
|
|
88
|
+
/** Custom renderer for tool details. Return ReactNode to override, null to use default. */
|
|
89
|
+
renderToolDetail?: CustomToolRenderer;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Full chat container: message list + auto-scroll + input area.
|
|
93
|
+
* Orchestrates useRunGroups, useRunCollapseState, and useAutoScroll.
|
|
94
|
+
*/
|
|
95
|
+
declare const ChatContainer: React.MemoExoticComponent<({ messages, partMap, isStreaming, onSend, branding, placeholder, className, hideInput, renderToolDetail, }: ChatContainerProps) => react_jsx_runtime.JSX.Element>;
|
|
96
|
+
|
|
97
|
+
interface MessageListProps {
|
|
98
|
+
groups: GroupedMessage[];
|
|
99
|
+
partMap: Record<string, SessionPart[]>;
|
|
100
|
+
isCollapsed: (runId: string) => boolean;
|
|
101
|
+
onToggleCollapse: (runId: string) => void;
|
|
102
|
+
branding?: AgentBranding;
|
|
103
|
+
renderToolDetail?: CustomToolRenderer;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Maps GroupedMessage[] to UserMessage and RunGroup components.
|
|
107
|
+
* This is the main render list for the chat view.
|
|
108
|
+
*/
|
|
109
|
+
declare const MessageList: React.MemoExoticComponent<({ groups, partMap, isCollapsed, onToggleCollapse, branding, renderToolDetail, }: MessageListProps) => react_jsx_runtime.JSX.Element>;
|
|
110
|
+
|
|
111
|
+
interface UserMessageProps {
|
|
112
|
+
message: SessionMessage;
|
|
113
|
+
parts: SessionPart[];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Simple user message bubble.
|
|
117
|
+
* Renders text parts from the user's message.
|
|
118
|
+
*/
|
|
119
|
+
declare const UserMessage: React.MemoExoticComponent<({ message, parts }: UserMessageProps) => react_jsx_runtime.JSX.Element | null>;
|
|
120
|
+
|
|
121
|
+
export { ChatContainer, type ChatContainerProps, ChatInput, type ChatInputProps, ChatMessage, ChatMessageList, type ChatMessageListProps, type ChatMessageProps, type Message, MessageList, type MessageListProps, type MessageRole, type PendingFile, ThinkingIndicator, UserMessage, type UserMessageProps };
|
package/dist/chat.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChatContainer,
|
|
3
|
+
ChatInput,
|
|
4
|
+
ChatMessage,
|
|
5
|
+
ChatMessageList,
|
|
6
|
+
MessageList,
|
|
7
|
+
ThinkingIndicator,
|
|
8
|
+
UserMessage
|
|
9
|
+
} from "./chunk-U62G5TS7.js";
|
|
10
|
+
import "./chunk-KMXV7DDX.js";
|
|
11
|
+
import "./chunk-6MQIDUPA.js";
|
|
12
|
+
import "./chunk-LTFK464G.js";
|
|
13
|
+
import "./chunk-M34OA6PQ.js";
|
|
14
|
+
import "./chunk-HRMUF35V.js";
|
|
15
|
+
import "./chunk-QSQBDR3N.js";
|
|
16
|
+
import "./chunk-RQHJBTEU.js";
|
|
17
|
+
export {
|
|
18
|
+
ChatContainer,
|
|
19
|
+
ChatInput,
|
|
20
|
+
ChatMessage,
|
|
21
|
+
ChatMessageList,
|
|
22
|
+
MessageList,
|
|
23
|
+
ThinkingIndicator,
|
|
24
|
+
UserMessage
|
|
25
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-RQHJBTEU.js";
|
|
4
|
+
|
|
5
|
+
// src/primitives/card.tsx
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var Card = React.forwardRef(({ className, variant = "default", hover = false, ...props }, ref) => {
|
|
9
|
+
const variants = {
|
|
10
|
+
default: "bg-card border-border",
|
|
11
|
+
glass: "bg-card/60 backdrop-blur-xl border-border/50",
|
|
12
|
+
sandbox: "bg-card/80 backdrop-blur-xl border-purple-500/20 shadow-lg shadow-purple-500/5"
|
|
13
|
+
};
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
"div",
|
|
16
|
+
{
|
|
17
|
+
ref,
|
|
18
|
+
className: cn(
|
|
19
|
+
"rounded-xl border text-card-foreground transition-all duration-300",
|
|
20
|
+
variants[variant],
|
|
21
|
+
hover && "cursor-pointer hover:border-muted-foreground/50 hover:shadow-xl",
|
|
22
|
+
className
|
|
23
|
+
),
|
|
24
|
+
...props
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
Card.displayName = "Card";
|
|
29
|
+
var CardHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
30
|
+
"div",
|
|
31
|
+
{
|
|
32
|
+
ref,
|
|
33
|
+
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
34
|
+
...props
|
|
35
|
+
}
|
|
36
|
+
));
|
|
37
|
+
CardHeader.displayName = "CardHeader";
|
|
38
|
+
var CardTitle = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
39
|
+
"h3",
|
|
40
|
+
{
|
|
41
|
+
ref,
|
|
42
|
+
className: cn("font-semibold leading-none tracking-tight", className),
|
|
43
|
+
...props
|
|
44
|
+
}
|
|
45
|
+
));
|
|
46
|
+
CardTitle.displayName = "CardTitle";
|
|
47
|
+
var CardDescription = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
48
|
+
"p",
|
|
49
|
+
{
|
|
50
|
+
ref,
|
|
51
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
52
|
+
...props
|
|
53
|
+
}
|
|
54
|
+
));
|
|
55
|
+
CardDescription.displayName = "CardDescription";
|
|
56
|
+
var CardContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
57
|
+
CardContent.displayName = "CardContent";
|
|
58
|
+
var CardFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
ref,
|
|
62
|
+
className: cn("flex items-center p-6 pt-0", className),
|
|
63
|
+
...props
|
|
64
|
+
}
|
|
65
|
+
));
|
|
66
|
+
CardFooter.displayName = "CardFooter";
|
|
67
|
+
|
|
68
|
+
// src/primitives/badge.tsx
|
|
69
|
+
import { cva } from "class-variance-authority";
|
|
70
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
71
|
+
var badgeVariants = cva(
|
|
72
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 font-semibold text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
73
|
+
{
|
|
74
|
+
variants: {
|
|
75
|
+
variant: {
|
|
76
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
77
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
|
78
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground",
|
|
79
|
+
outline: "text-foreground",
|
|
80
|
+
success: "border-green-500/20 border-transparent bg-green-500/10 text-green-400",
|
|
81
|
+
warning: "border-transparent border-yellow-500/20 bg-yellow-500/10 text-yellow-400",
|
|
82
|
+
error: "border-red-500/20 border-transparent bg-red-500/10 text-red-400",
|
|
83
|
+
info: "border-blue-500/20 border-transparent bg-blue-500/10 text-blue-400",
|
|
84
|
+
sandbox: "border-purple-500/20 border-transparent bg-purple-500/10 text-purple-400"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
defaultVariants: {
|
|
88
|
+
variant: "default"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
function Badge({ className, variant, ...props }) {
|
|
93
|
+
return /* @__PURE__ */ jsx2("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/primitives/progress.tsx
|
|
97
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
98
|
+
import * as React2 from "react";
|
|
99
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
100
|
+
var Progress = React2.forwardRef(
|
|
101
|
+
({ className, value, variant = "default", showValue = false, ...props }, ref) => {
|
|
102
|
+
const indicatorVariants = {
|
|
103
|
+
default: "bg-primary",
|
|
104
|
+
sandbox: "bg-gradient-to-r from-purple-500 to-violet-500"
|
|
105
|
+
};
|
|
106
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
107
|
+
/* @__PURE__ */ jsx3(
|
|
108
|
+
ProgressPrimitive.Root,
|
|
109
|
+
{
|
|
110
|
+
ref,
|
|
111
|
+
className: cn(
|
|
112
|
+
"relative h-2 w-full overflow-hidden rounded-full bg-muted",
|
|
113
|
+
className
|
|
114
|
+
),
|
|
115
|
+
...props,
|
|
116
|
+
children: /* @__PURE__ */ jsx3(
|
|
117
|
+
ProgressPrimitive.Indicator,
|
|
118
|
+
{
|
|
119
|
+
className: cn(
|
|
120
|
+
"h-full w-full flex-1 transition-all duration-300 ease-out",
|
|
121
|
+
indicatorVariants[variant]
|
|
122
|
+
),
|
|
123
|
+
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
showValue && /* @__PURE__ */ jsxs("span", { className: "absolute -top-6 right-0 text-muted-foreground text-xs", children: [
|
|
129
|
+
value,
|
|
130
|
+
"%"
|
|
131
|
+
] })
|
|
132
|
+
] });
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
136
|
+
|
|
137
|
+
// src/primitives/skeleton.tsx
|
|
138
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
139
|
+
function Skeleton({
|
|
140
|
+
className,
|
|
141
|
+
...props
|
|
142
|
+
}) {
|
|
143
|
+
return /* @__PURE__ */ jsx4(
|
|
144
|
+
"div",
|
|
145
|
+
{
|
|
146
|
+
className: cn("animate-pulse rounded-lg bg-muted", className),
|
|
147
|
+
...props
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function SkeletonCard({ className }) {
|
|
152
|
+
return /* @__PURE__ */ jsxs2(
|
|
153
|
+
"div",
|
|
154
|
+
{
|
|
155
|
+
className: cn(
|
|
156
|
+
"space-y-4 rounded-xl border border-border bg-card p-6",
|
|
157
|
+
className
|
|
158
|
+
),
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-3/4" }),
|
|
161
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/2" }),
|
|
162
|
+
/* @__PURE__ */ jsxs2("div", { className: "space-y-2 pt-4", children: [
|
|
163
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-3 w-full" }),
|
|
164
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-3 w-5/6" }),
|
|
165
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-3 w-4/6" })
|
|
166
|
+
] })
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
function SkeletonTable({ rows = 5 }) {
|
|
172
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
|
|
173
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 border-border border-b pb-2", children: [
|
|
174
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" }),
|
|
175
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" }),
|
|
176
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" }),
|
|
177
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" })
|
|
178
|
+
] }),
|
|
179
|
+
Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs2("div", { className: "flex gap-4", children: [
|
|
180
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" }),
|
|
181
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" }),
|
|
182
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" }),
|
|
183
|
+
/* @__PURE__ */ jsx4(Skeleton, { className: "h-4 w-1/4" })
|
|
184
|
+
] }, i))
|
|
185
|
+
] });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export {
|
|
189
|
+
Card,
|
|
190
|
+
CardHeader,
|
|
191
|
+
CardTitle,
|
|
192
|
+
CardDescription,
|
|
193
|
+
CardContent,
|
|
194
|
+
CardFooter,
|
|
195
|
+
badgeVariants,
|
|
196
|
+
Badge,
|
|
197
|
+
Progress,
|
|
198
|
+
Skeleton,
|
|
199
|
+
SkeletonCard,
|
|
200
|
+
SkeletonTable
|
|
201
|
+
};
|