@tangle-network/sandbox-ui 0.2.2 → 0.3.4

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.
Files changed (70) hide show
  1. package/README.md +201 -10
  2. package/dist/auth.js +2 -2
  3. package/dist/chat-container-C8eHLw8z.d.ts +67 -0
  4. package/dist/chat.d.ts +70 -78
  5. package/dist/chat.js +8 -8
  6. package/dist/chunk-4F2GJRGU.js +756 -0
  7. package/dist/{chunk-HYEAX3DC.js → chunk-5LV6DZZF.js} +445 -114
  8. package/dist/{chunk-QSQBDR3N.js → chunk-BX6AQMUS.js} +5 -2
  9. package/dist/chunk-CCKNIAS7.js +124 -0
  10. package/dist/chunk-CJ2RYVZH.js +128 -0
  11. package/dist/{chunk-KMXV7DDX.js → chunk-CNWVHQFY.js} +1 -1
  12. package/dist/{chunk-OU4TRNQZ.js → chunk-COCSO7FG.js} +3 -3
  13. package/dist/chunk-FJSVPBKY.js +85 -0
  14. package/dist/chunk-FRGMMANX.js +102 -0
  15. package/dist/{chunk-E6FS7R4X.js → chunk-HWLX5NME.js} +1 -1
  16. package/dist/chunk-HY5IBRCE.js +1171 -0
  17. package/dist/chunk-JF6E2DS5.js +610 -0
  18. package/dist/chunk-MUOL44AE.js +121 -0
  19. package/dist/chunk-MXCSSOGH.js +105 -0
  20. package/dist/{chunk-NI2EI43H.js → chunk-PDV7W4NY.js} +9 -124
  21. package/dist/chunk-QGI5E7JD.js +1106 -0
  22. package/dist/chunk-TQN3VR4F.js +92 -0
  23. package/dist/{chunk-SOT2V7TX.js → chunk-TXI4MZAZ.js} +62 -144
  24. package/dist/chunk-WUR652Y3.js +1140 -0
  25. package/dist/chunk-YDBXQQLC.js +336 -0
  26. package/dist/{chunk-4EIWPJMJ.js → chunk-ZP6GSX4D.js} +36 -27
  27. package/dist/dashboard.d.ts +4 -111
  28. package/dist/dashboard.js +17 -4
  29. package/dist/{expanded-tool-detail-OkXGqTHe.d.ts → expanded-tool-detail-BDi_h_dZ.d.ts} +11 -4
  30. package/dist/file-tabs-CmaoDVBI.d.ts +72 -0
  31. package/dist/files.d.ts +25 -44
  32. package/dist/files.js +8 -3
  33. package/{src/styles → dist}/globals.css +16 -67
  34. package/dist/hooks.d.ts +5 -4
  35. package/dist/hooks.js +14 -9
  36. package/dist/index-BOjBJwzD.d.ts +219 -0
  37. package/dist/index.d.ts +39 -10
  38. package/dist/index.js +190 -121
  39. package/dist/markdown.d.ts +1 -24
  40. package/dist/markdown.js +1 -7
  41. package/dist/openui.d.ts +115 -0
  42. package/dist/openui.js +11 -0
  43. package/dist/pages.d.ts +3 -2
  44. package/dist/pages.js +19 -16
  45. package/dist/primitives.js +25 -19
  46. package/dist/run.d.ts +2 -2
  47. package/dist/run.js +8 -7
  48. package/dist/{use-sidecar-auth-Bb0-w3lX.d.ts → sdk-hooks.d.ts} +61 -72
  49. package/dist/sdk-hooks.js +29 -0
  50. package/dist/styles.css +179 -0
  51. package/dist/tokens.css +275 -0
  52. package/dist/{tool-display-BvsVW_Ur.d.ts → tool-display-Ct9nFAzJ.d.ts} +1 -1
  53. package/dist/types.d.ts +1 -1
  54. package/dist/{usage-chart-DINgSVL5.d.ts → usage-chart-CY9xo3KX.d.ts} +8 -3
  55. package/dist/use-pty-session-DeZSxOCN.d.ts +69 -0
  56. package/dist/utils.d.ts +1 -1
  57. package/dist/utils.js +1 -1
  58. package/dist/workspace.d.ts +171 -33
  59. package/dist/workspace.js +25 -1
  60. package/package.json +10 -3
  61. package/tailwind.config.cjs +39 -0
  62. package/dist/chunk-2UHPE5T7.js +0 -201
  63. package/dist/chunk-6MQIDUPA.js +0 -502
  64. package/dist/chunk-J4OADEUK.js +0 -814
  65. package/dist/chunk-KYY2X6LY.js +0 -318
  66. package/dist/chunk-L6ZDH5F4.js +0 -334
  67. package/dist/chunk-M34OA6PQ.js +0 -233
  68. package/dist/chunk-M6VLC32S.js +0 -219
  69. package/dist/chunk-U62G5TS7.js +0 -472
  70. package/src/styles/tokens.css +0 -73
@@ -0,0 +1,124 @@
1
+ import {
2
+ ToolCallGroup,
3
+ ToolCallStep
4
+ } from "./chunk-CJ2RYVZH.js";
5
+ import {
6
+ Markdown
7
+ } from "./chunk-LTFK464G.js";
8
+ import {
9
+ cn
10
+ } from "./chunk-RQHJBTEU.js";
11
+
12
+ // src/run/tool-call-feed.tsx
13
+ import { jsx } from "react/jsx-runtime";
14
+ function ToolCallFeed({ segments, className }) {
15
+ if (segments.length === 0) return null;
16
+ return /* @__PURE__ */ jsx("div", { className: cn("space-y-1", className), children: segments.map((segment, i) => {
17
+ if (segment.kind === "text") {
18
+ return segment.content.trim() ? /* @__PURE__ */ jsx(Markdown, { children: segment.content }, i) : null;
19
+ }
20
+ if (segment.kind === "tool_call") {
21
+ return /* @__PURE__ */ jsx(
22
+ ToolCallStep,
23
+ {
24
+ type: segment.call.type,
25
+ label: segment.call.label,
26
+ status: segment.call.status,
27
+ detail: segment.call.detail,
28
+ output: segment.call.output,
29
+ duration: segment.call.duration
30
+ },
31
+ segment.call.id || i
32
+ );
33
+ }
34
+ if (segment.kind === "tool_group") {
35
+ return /* @__PURE__ */ jsx(ToolCallGroup, { title: segment.title, children: segment.calls.map((call) => /* @__PURE__ */ jsx(
36
+ ToolCallStep,
37
+ {
38
+ type: call.type,
39
+ label: call.label,
40
+ status: call.status,
41
+ detail: call.detail,
42
+ output: call.output,
43
+ duration: call.duration
44
+ },
45
+ call.id
46
+ )) }, i);
47
+ }
48
+ return null;
49
+ }) });
50
+ }
51
+ function parseToolEvent(event) {
52
+ const { type, data } = event;
53
+ if (type === "tool.invocation" || type === "tool_use") {
54
+ const toolName = data.name || data.tool || "unknown";
55
+ const input = data.input;
56
+ return {
57
+ id: data.id || data.toolUseId || crypto.randomUUID(),
58
+ type: mapToolName(toolName),
59
+ label: formatToolLabel(toolName, input),
60
+ status: "running",
61
+ detail: input ? formatToolInput(toolName, input) : void 0
62
+ };
63
+ }
64
+ if (type === "tool.result" || type === "tool_result") {
65
+ return {
66
+ id: data.id || data.toolUseId || "",
67
+ type: mapToolName(data.name || data.tool || "unknown"),
68
+ label: formatToolLabel(data.name || data.tool || "unknown"),
69
+ status: data.error ? "error" : "success",
70
+ output: truncate(data.output || data.result || "", 500),
71
+ duration: data.duration
72
+ };
73
+ }
74
+ return null;
75
+ }
76
+ function mapToolName(name) {
77
+ const lower = name.toLowerCase();
78
+ if (lower.includes("bash") || lower.includes("terminal") || lower.includes("exec")) return "bash";
79
+ if (lower.includes("read") || lower.includes("cat")) return "read";
80
+ if (lower.includes("write") || lower.includes("create")) return "write";
81
+ if (lower.includes("edit") || lower.includes("replace")) return "edit";
82
+ if (lower.includes("glob") || lower.includes("find")) return "glob";
83
+ if (lower.includes("grep") || lower.includes("search")) return "grep";
84
+ if (lower.includes("list") || lower.includes("ls")) return "list";
85
+ if (lower.includes("inspect")) return "inspect";
86
+ if (lower.includes("audit")) return "audit";
87
+ return "unknown";
88
+ }
89
+ function formatToolLabel(toolName, input) {
90
+ const lower = toolName.toLowerCase();
91
+ if (lower.includes("bash") && input?.command) {
92
+ const cmd = String(input.command);
93
+ return cmd.length > 80 ? `${cmd.slice(0, 77)}...` : cmd;
94
+ }
95
+ if ((lower.includes("read") || lower.includes("cat")) && input?.path) {
96
+ return `Read ${input.path}`;
97
+ }
98
+ if (lower.includes("write") && input?.path) {
99
+ return `Write ${input.path}`;
100
+ }
101
+ if (lower.includes("edit") && input?.path) {
102
+ return `Edit ${input.path}`;
103
+ }
104
+ if (lower.includes("glob") && input?.pattern) {
105
+ return `Find ${input.pattern}`;
106
+ }
107
+ if (lower.includes("grep") && input?.pattern) {
108
+ return `Search for ${input.pattern}`;
109
+ }
110
+ return toolName;
111
+ }
112
+ function formatToolInput(toolName, input) {
113
+ if (input.command) return String(input.command);
114
+ if (input.path) return String(input.path);
115
+ return JSON.stringify(input, null, 2).slice(0, 300);
116
+ }
117
+ function truncate(text, max) {
118
+ return text.length > max ? text.slice(0, max - 3) + "..." : text;
119
+ }
120
+
121
+ export {
122
+ ToolCallFeed,
123
+ parseToolEvent
124
+ };
@@ -0,0 +1,128 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/run/tool-call-step.tsx
6
+ import { useState } from "react";
7
+ import {
8
+ Terminal,
9
+ FileText,
10
+ FileCode,
11
+ Search,
12
+ CheckCircle,
13
+ ChevronRight,
14
+ Loader2,
15
+ FolderOpen,
16
+ Download,
17
+ Pencil,
18
+ Eye
19
+ } from "lucide-react";
20
+ import { jsx, jsxs } from "react/jsx-runtime";
21
+ var ICONS = {
22
+ bash: Terminal,
23
+ read: Eye,
24
+ write: FileText,
25
+ edit: Pencil,
26
+ glob: FolderOpen,
27
+ grep: Search,
28
+ list: FolderOpen,
29
+ download: Download,
30
+ inspect: Search,
31
+ audit: CheckCircle,
32
+ unknown: FileCode
33
+ };
34
+ var STATUS_COLORS = {
35
+ running: "text-[var(--brand-cool)]",
36
+ success: "text-[var(--code-success)]",
37
+ error: "text-[var(--code-error)]"
38
+ };
39
+ function ToolCallStep({
40
+ type,
41
+ label,
42
+ status,
43
+ detail,
44
+ output,
45
+ duration,
46
+ className
47
+ }) {
48
+ const [expanded, setExpanded] = useState(false);
49
+ const Icon = ICONS[type] || ICONS.unknown;
50
+ const hasExpandable = !!(detail || output);
51
+ return /* @__PURE__ */ jsxs(
52
+ "div",
53
+ {
54
+ className: cn(
55
+ "group overflow-hidden rounded-[var(--radius-lg)] border bg-[var(--bg-card)] transition-colors",
56
+ status === "running" && "border-[var(--border-accent)]",
57
+ status === "success" && "border-[var(--border-subtle)] hover:border-[var(--border-accent)]",
58
+ status === "error" && "border-red-500/30",
59
+ className
60
+ ),
61
+ children: [
62
+ /* @__PURE__ */ jsxs(
63
+ "button",
64
+ {
65
+ onClick: () => hasExpandable && setExpanded(!expanded),
66
+ disabled: !hasExpandable,
67
+ className: cn(
68
+ "flex w-full items-center gap-3 px-3 py-3 text-left text-sm",
69
+ hasExpandable && "cursor-pointer"
70
+ ),
71
+ children: [
72
+ /* @__PURE__ */ jsx(
73
+ "div",
74
+ {
75
+ className: cn(
76
+ "flex h-8 w-8 shrink-0 items-center justify-center rounded-[var(--radius-md)] border",
77
+ status === "running" && "border-[var(--border-accent)] bg-[var(--brand-cool)]/12 text-[var(--brand-cool)]",
78
+ status === "success" && "border-emerald-500/30 bg-emerald-500/10 text-emerald-200",
79
+ status === "error" && "border-red-500/30 bg-red-500/10 text-red-200"
80
+ ),
81
+ children: status === "running" ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin shrink-0" }) : /* @__PURE__ */ jsx(Icon, { className: cn("h-4 w-4 shrink-0", STATUS_COLORS[status]) })
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsx("span", { className: "truncate flex-1 font-[var(--font-sans)] text-[var(--text-secondary)]", children: label }),
85
+ /* @__PURE__ */ jsx(
86
+ "span",
87
+ {
88
+ className: cn(
89
+ "rounded-full border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-[0.06em]",
90
+ status === "running" && "border-[var(--border-accent)] bg-[var(--brand-cool)]/10 text-[var(--brand-cool)]",
91
+ status === "success" && "border-emerald-500/30 bg-emerald-500/10 text-emerald-200",
92
+ status === "error" && "border-red-500/30 bg-red-500/10 text-red-200"
93
+ ),
94
+ children: status
95
+ }
96
+ ),
97
+ duration !== void 0 && status !== "running" && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-[var(--text-muted)]", children: duration < 1e3 ? `${duration}ms` : `${(duration / 1e3).toFixed(1)}s` }),
98
+ hasExpandable && /* @__PURE__ */ jsx(
99
+ ChevronRight,
100
+ {
101
+ className: cn(
102
+ "h-3 w-3 text-[var(--text-muted)] transition-transform shrink-0",
103
+ expanded && "rotate-90"
104
+ )
105
+ }
106
+ )
107
+ ]
108
+ }
109
+ ),
110
+ expanded && (detail || output) && /* @__PURE__ */ jsxs("div", { className: "space-y-2 border-t border-[var(--border-subtle)] bg-[var(--bg-section)]/50 px-4 py-4", children: [
111
+ detail && /* @__PURE__ */ jsx("div", { className: "text-xs font-[var(--font-mono)] text-[var(--text-muted)]", children: detail }),
112
+ output && /* @__PURE__ */ jsx("pre", { className: "max-h-48 overflow-x-auto rounded-[var(--radius-md)] border border-[var(--border-subtle)] bg-[var(--bg-input)] p-3 text-xs font-[var(--font-mono)] text-[var(--text-secondary)]", children: output })
113
+ ] })
114
+ ]
115
+ }
116
+ );
117
+ }
118
+ function ToolCallGroup({ title, children, className }) {
119
+ return /* @__PURE__ */ jsxs("div", { className: cn("my-2 space-y-2", className), children: [
120
+ title && /* @__PURE__ */ jsx("div", { className: "mb-1 px-1 text-xs font-medium uppercase tracking-wider text-[var(--text-muted)]", children: title }),
121
+ children
122
+ ] });
123
+ }
124
+
125
+ export {
126
+ ToolCallStep,
127
+ ToolCallGroup
128
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getToolCategory
3
- } from "./chunk-QSQBDR3N.js";
3
+ } from "./chunk-BX6AQMUS.js";
4
4
 
5
5
  // src/hooks/use-run-groups.ts
6
6
  import { useMemo } from "react";
@@ -13,7 +13,7 @@ import {
13
13
  } from "./chunk-MCGKDCOR.js";
14
14
  import {
15
15
  Button
16
- } from "./chunk-E6FS7R4X.js";
16
+ } from "./chunk-HWLX5NME.js";
17
17
  import {
18
18
  cn
19
19
  } from "./chunk-RQHJBTEU.js";
@@ -71,7 +71,7 @@ function UserMenu({
71
71
  }) {
72
72
  const initials = user.name ? user.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : user.email.slice(0, 2).toUpperCase();
73
73
  const avatarGradient = {
74
- sandbox: "from-purple-500 to-violet-500"
74
+ sandbox: "bg-[image:var(--accent-gradient-strong)]"
75
75
  }[variant];
76
76
  const handleLogout = () => {
77
77
  if (onLogout) {
@@ -92,7 +92,7 @@ function UserMenu({
92
92
  AvatarFallback,
93
93
  {
94
94
  className: cn(
95
- "bg-gradient-to-r text-white text-xs",
95
+ "text-white text-xs",
96
96
  avatarGradient
97
97
  ),
98
98
  children: initials
@@ -0,0 +1,85 @@
1
+ // src/hooks/use-auth.ts
2
+ import * as React from "react";
3
+ function useAuth({
4
+ apiBaseUrl,
5
+ revalidateOnFocus = false,
6
+ shouldRetryOnError = false
7
+ }) {
8
+ const [user, setUser] = React.useState(null);
9
+ const [isLoading, setIsLoading] = React.useState(true);
10
+ const [error, setError] = React.useState(null);
11
+ const fetchSession = React.useCallback(async () => {
12
+ setIsLoading(true);
13
+ setError(null);
14
+ try {
15
+ const res = await fetch(`${apiBaseUrl}/auth/session`, {
16
+ credentials: "include"
17
+ });
18
+ if (!res.ok) {
19
+ throw new Error("Not authenticated");
20
+ }
21
+ const data = await res.json();
22
+ if (data.success && data.data) {
23
+ setUser(data.data);
24
+ } else {
25
+ setUser(null);
26
+ }
27
+ } catch (err) {
28
+ setError(err instanceof Error ? err : new Error("Unknown error"));
29
+ setUser(null);
30
+ if (shouldRetryOnError) {
31
+ setTimeout(fetchSession, 5e3);
32
+ }
33
+ } finally {
34
+ setIsLoading(false);
35
+ }
36
+ }, [apiBaseUrl, shouldRetryOnError]);
37
+ React.useEffect(() => {
38
+ fetchSession();
39
+ }, [fetchSession]);
40
+ React.useEffect(() => {
41
+ if (!revalidateOnFocus) return;
42
+ const handleFocus = () => {
43
+ fetchSession();
44
+ };
45
+ window.addEventListener("focus", handleFocus);
46
+ return () => window.removeEventListener("focus", handleFocus);
47
+ }, [revalidateOnFocus, fetchSession]);
48
+ return {
49
+ user,
50
+ isLoading,
51
+ isError: !!error,
52
+ error,
53
+ mutate: fetchSession
54
+ };
55
+ }
56
+ function createAuthFetcher(_apiBaseUrl) {
57
+ return async function authFetcher(url, options) {
58
+ const res = await fetch(url, {
59
+ ...options,
60
+ credentials: "include",
61
+ headers: {
62
+ ...options?.headers
63
+ }
64
+ });
65
+ if (!res.ok) {
66
+ throw new Error(`Request failed with status ${res.status}`);
67
+ }
68
+ return res.json();
69
+ };
70
+ }
71
+ function useApiKey() {
72
+ const [apiKey, setApiKey] = React.useState(null);
73
+ React.useEffect(() => {
74
+ if (typeof window !== "undefined") {
75
+ setApiKey(localStorage.getItem("apiKey"));
76
+ }
77
+ }, []);
78
+ return apiKey;
79
+ }
80
+
81
+ export {
82
+ useAuth,
83
+ createAuthFetcher,
84
+ useApiKey
85
+ };
@@ -0,0 +1,102 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/primitives/progress.tsx
6
+ import * as ProgressPrimitive from "@radix-ui/react-progress";
7
+ import * as React from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var Progress = React.forwardRef(
10
+ ({ className, value, variant = "default", showValue = false, ...props }, ref) => {
11
+ const indicatorVariants = {
12
+ default: "bg-primary",
13
+ sandbox: "bg-[image:var(--accent-gradient-strong)]"
14
+ };
15
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
16
+ /* @__PURE__ */ jsx(
17
+ ProgressPrimitive.Root,
18
+ {
19
+ ref,
20
+ className: cn(
21
+ "relative h-2 w-full overflow-hidden rounded-full bg-muted",
22
+ className
23
+ ),
24
+ ...props,
25
+ children: /* @__PURE__ */ jsx(
26
+ ProgressPrimitive.Indicator,
27
+ {
28
+ className: cn(
29
+ "h-full w-full flex-1 transition-all duration-300 ease-out",
30
+ indicatorVariants[variant]
31
+ ),
32
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
33
+ }
34
+ )
35
+ }
36
+ ),
37
+ showValue && /* @__PURE__ */ jsxs("span", { className: "absolute -top-6 right-0 text-muted-foreground text-xs", children: [
38
+ value,
39
+ "%"
40
+ ] })
41
+ ] });
42
+ }
43
+ );
44
+ Progress.displayName = ProgressPrimitive.Root.displayName;
45
+
46
+ // src/primitives/skeleton.tsx
47
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
48
+ function Skeleton({
49
+ className,
50
+ ...props
51
+ }) {
52
+ return /* @__PURE__ */ jsx2(
53
+ "div",
54
+ {
55
+ className: cn("animate-pulse rounded-lg bg-muted", className),
56
+ ...props
57
+ }
58
+ );
59
+ }
60
+ function SkeletonCard({ className }) {
61
+ return /* @__PURE__ */ jsxs2(
62
+ "div",
63
+ {
64
+ className: cn(
65
+ "space-y-4 rounded-xl border border-border bg-card p-6",
66
+ className
67
+ ),
68
+ children: [
69
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-3/4" }),
70
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/2" }),
71
+ /* @__PURE__ */ jsxs2("div", { className: "space-y-2 pt-4", children: [
72
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-3 w-full" }),
73
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-3 w-5/6" }),
74
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-3 w-4/6" })
75
+ ] })
76
+ ]
77
+ }
78
+ );
79
+ }
80
+ function SkeletonTable({ rows = 5 }) {
81
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
82
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-4 border-border border-b pb-2", children: [
83
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
84
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
85
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
86
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" })
87
+ ] }),
88
+ Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs2("div", { className: "flex gap-4", children: [
89
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
90
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
91
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
92
+ /* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" })
93
+ ] }, i))
94
+ ] });
95
+ }
96
+
97
+ export {
98
+ Progress,
99
+ Skeleton,
100
+ SkeletonCard,
101
+ SkeletonTable
102
+ };
@@ -18,7 +18,7 @@ var buttonVariants = cva(
18
18
  secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 active:scale-[0.98]",
19
19
  ghost: "hover:bg-accent hover:text-accent-foreground",
20
20
  link: "text-primary underline-offset-4 hover:underline",
21
- sandbox: "bg-gradient-to-r from-purple-600 to-violet-500 text-white shadow-lg shadow-purple-500/25 hover:from-purple-700 hover:to-violet-600 hover:shadow-purple-500/40 active:scale-[0.98]"
21
+ sandbox: "bg-[image:var(--accent-gradient-strong)] text-white shadow-[var(--shadow-accent)] hover:brightness-110 active:scale-[0.98]"
22
22
  },
23
23
  size: {
24
24
  default: "h-10 px-4 py-2",