@tangle-network/ui 1.0.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.
Files changed (220) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +33 -0
  4. package/dist/active-sessions-store-CeOmXgv5.d.ts +85 -0
  5. package/dist/artifact-pane-DvJyPWV4.d.ts +24 -0
  6. package/dist/auth.d.ts +74 -0
  7. package/dist/auth.js +15 -0
  8. package/dist/button-CMQuQEW_.d.ts +17 -0
  9. package/dist/chat.d.ts +232 -0
  10. package/dist/chat.js +30 -0
  11. package/dist/chunk-2NFQRQOD.js +1009 -0
  12. package/dist/chunk-2VH6PUXD.js +186 -0
  13. package/dist/chunk-34A66VBG.js +214 -0
  14. package/dist/chunk-3OI2QKFD.js +0 -0
  15. package/dist/chunk-4CLN43XT.js +45 -0
  16. package/dist/chunk-54SQQMMM.js +156 -0
  17. package/dist/chunk-5Z5ZYMOJ.js +0 -0
  18. package/dist/chunk-66BNMOVT.js +167 -0
  19. package/dist/chunk-6BGQA4BQ.js +0 -0
  20. package/dist/chunk-7UO2ZMRQ.js +133 -0
  21. package/dist/chunk-BX6AQMUS.js +183 -0
  22. package/dist/chunk-CD53GZOM.js +59 -0
  23. package/dist/chunk-CSAIKY36.js +54 -0
  24. package/dist/chunk-EEE55AVS.js +1201 -0
  25. package/dist/chunk-GYPQXTJU.js +230 -0
  26. package/dist/chunk-HFL6R6IF.js +37 -0
  27. package/dist/chunk-HJKCSXCH.js +737 -0
  28. package/dist/chunk-LISXUB4D.js +1222 -0
  29. package/dist/chunk-LQS34IGP.js +0 -0
  30. package/dist/chunk-MKTSMWVD.js +109 -0
  31. package/dist/chunk-NKDZ7GZE.js +192 -0
  32. package/dist/chunk-OEX7NZE3.js +321 -0
  33. package/dist/chunk-Q56BYXQF.js +61 -0
  34. package/dist/chunk-Q7EIIWTC.js +0 -0
  35. package/dist/chunk-REJESC5U.js +117 -0
  36. package/dist/chunk-RQGKSCEZ.js +0 -0
  37. package/dist/chunk-RQHJBTEU.js +10 -0
  38. package/dist/chunk-TMFOPHHN.js +299 -0
  39. package/dist/chunk-XGKULLYE.js +40 -0
  40. package/dist/chunk-XIHMJ7ZQ.js +614 -0
  41. package/dist/chunk-YJ2G3XO5.js +1048 -0
  42. package/dist/chunk-YNN4O57I.js +754 -0
  43. package/dist/code-block-DjXf8eOG.d.ts +19 -0
  44. package/dist/document-editor-pane-A5LT5H4N.js +12 -0
  45. package/dist/document-editor-pane-DyDEX_Zm.d.ts +124 -0
  46. package/dist/editor.d.ts +120 -0
  47. package/dist/editor.js +34 -0
  48. package/dist/files.d.ts +175 -0
  49. package/dist/files.js +20 -0
  50. package/dist/hooks.d.ts +56 -0
  51. package/dist/hooks.js +41 -0
  52. package/dist/index.d.ts +43 -0
  53. package/dist/index.js +446 -0
  54. package/dist/markdown.d.ts +15 -0
  55. package/dist/markdown.js +14 -0
  56. package/dist/message-BHWbxBtT.d.ts +15 -0
  57. package/dist/openui.d.ts +115 -0
  58. package/dist/openui.js +12 -0
  59. package/dist/parts-dj7AcUg0.d.ts +36 -0
  60. package/dist/primitives.d.ts +332 -0
  61. package/dist/primitives.js +191 -0
  62. package/dist/run-PfLmDAox.d.ts +41 -0
  63. package/dist/run.d.ts +69 -0
  64. package/dist/run.js +36 -0
  65. package/dist/sdk-hooks.d.ts +285 -0
  66. package/dist/sdk-hooks.js +31 -0
  67. package/dist/stores.d.ts +17 -0
  68. package/dist/stores.js +76 -0
  69. package/dist/tool-call-feed-Bs3MyQMT.d.ts +68 -0
  70. package/dist/tool-display-z4JcDmMQ.d.ts +32 -0
  71. package/dist/tool-previews.d.ts +48 -0
  72. package/dist/tool-previews.js +21 -0
  73. package/dist/types.d.ts +19 -0
  74. package/dist/types.js +1 -0
  75. package/dist/utils.d.ts +45 -0
  76. package/dist/utils.js +32 -0
  77. package/package.json +193 -0
  78. package/src/auth/auth.tsx +228 -0
  79. package/src/auth/index.ts +13 -0
  80. package/src/auth/login-layout.tsx +46 -0
  81. package/src/chat/agent-timeline.stories.tsx +429 -0
  82. package/src/chat/agent-timeline.tsx +360 -0
  83. package/src/chat/chat-container.tsx +486 -0
  84. package/src/chat/chat-input.stories.tsx +142 -0
  85. package/src/chat/chat-input.tsx +389 -0
  86. package/src/chat/chat-message.stories.tsx +237 -0
  87. package/src/chat/chat-message.tsx +129 -0
  88. package/src/chat/index.ts +18 -0
  89. package/src/chat/message-list.stories.tsx +336 -0
  90. package/src/chat/message-list.tsx +79 -0
  91. package/src/chat/thinking-indicator.stories.tsx +56 -0
  92. package/src/chat/thinking-indicator.tsx +30 -0
  93. package/src/chat/user-message.stories.tsx +92 -0
  94. package/src/chat/user-message.tsx +43 -0
  95. package/src/editor/document-editor-pane.tsx +351 -0
  96. package/src/editor/editor-provider.tsx +428 -0
  97. package/src/editor/editor-toolbar.tsx +130 -0
  98. package/src/editor/index.ts +31 -0
  99. package/src/editor/markdown-conversion.ts +21 -0
  100. package/src/editor/markdown-document-editor.tsx +137 -0
  101. package/src/editor/tiptap-editor.tsx +331 -0
  102. package/src/editor/use-editor.ts +221 -0
  103. package/src/files/file-artifact-pane.tsx +183 -0
  104. package/src/files/file-preview.tsx +342 -0
  105. package/src/files/file-tabs.tsx +71 -0
  106. package/src/files/file-tree.tsx +258 -0
  107. package/src/files/index.ts +17 -0
  108. package/src/files/rich-file-tree.stories.tsx +104 -0
  109. package/src/files/rich-file-tree.test.tsx +42 -0
  110. package/src/files/rich-file-tree.tsx +232 -0
  111. package/src/hooks/index.ts +10 -0
  112. package/src/hooks/use-auth.ts +153 -0
  113. package/src/hooks/use-auto-scroll.ts +59 -0
  114. package/src/hooks/use-dropdown-menu.ts +40 -0
  115. package/src/hooks/use-live-time.test.tsx +40 -0
  116. package/src/hooks/use-live-time.ts +27 -0
  117. package/src/hooks/use-realtime-session.ts +319 -0
  118. package/src/hooks/use-run-collapse-state.ts +25 -0
  119. package/src/hooks/use-run-groups.ts +111 -0
  120. package/src/hooks/use-sdk-session.ts +575 -0
  121. package/src/hooks/use-sse-stream.ts +475 -0
  122. package/src/hooks/use-tool-call-stream.ts +96 -0
  123. package/src/index.ts +14 -0
  124. package/src/lib/utils.ts +6 -0
  125. package/src/markdown/code-block.tsx +198 -0
  126. package/src/markdown/index.ts +2 -0
  127. package/src/markdown/markdown.stories.tsx +190 -0
  128. package/src/markdown/markdown.tsx +62 -0
  129. package/src/openui/index.ts +20 -0
  130. package/src/openui/openui-artifact-renderer.tsx +542 -0
  131. package/src/primitives/artifact-pane.tsx +91 -0
  132. package/src/primitives/avatar.stories.tsx +95 -0
  133. package/src/primitives/avatar.tsx +47 -0
  134. package/src/primitives/badge.stories.tsx +57 -0
  135. package/src/primitives/badge.tsx +97 -0
  136. package/src/primitives/button.stories.tsx +48 -0
  137. package/src/primitives/button.tsx +115 -0
  138. package/src/primitives/card.stories.tsx +53 -0
  139. package/src/primitives/card.tsx +98 -0
  140. package/src/primitives/code-block.stories.tsx +115 -0
  141. package/src/primitives/code-block.tsx +22 -0
  142. package/src/primitives/design-tokens.stories.tsx +162 -0
  143. package/src/primitives/dialog.stories.tsx +176 -0
  144. package/src/primitives/dialog.tsx +137 -0
  145. package/src/primitives/drop-zone.stories.tsx +123 -0
  146. package/src/primitives/drop-zone.tsx +131 -0
  147. package/src/primitives/dropdown-menu.stories.tsx +122 -0
  148. package/src/primitives/dropdown-menu.tsx +214 -0
  149. package/src/primitives/empty-state.stories.tsx +81 -0
  150. package/src/primitives/empty-state.tsx +40 -0
  151. package/src/primitives/index.ts +118 -0
  152. package/src/primitives/input.stories.tsx +113 -0
  153. package/src/primitives/input.tsx +136 -0
  154. package/src/primitives/label.stories.tsx +84 -0
  155. package/src/primitives/label.tsx +24 -0
  156. package/src/primitives/progress.stories.tsx +93 -0
  157. package/src/primitives/progress.tsx +50 -0
  158. package/src/primitives/segmented-control.test.tsx +328 -0
  159. package/src/primitives/segmented-control.tsx +154 -0
  160. package/src/primitives/select.stories.tsx +164 -0
  161. package/src/primitives/select.tsx +158 -0
  162. package/src/primitives/sidebar-drop-zone.stories.tsx +100 -0
  163. package/src/primitives/sidebar-drop-zone.tsx +149 -0
  164. package/src/primitives/skeleton.stories.tsx +79 -0
  165. package/src/primitives/skeleton.tsx +55 -0
  166. package/src/primitives/stat-card.stories.tsx +137 -0
  167. package/src/primitives/stat-card.tsx +97 -0
  168. package/src/primitives/switch.stories.tsx +85 -0
  169. package/src/primitives/switch.tsx +28 -0
  170. package/src/primitives/table.stories.tsx +170 -0
  171. package/src/primitives/table.tsx +116 -0
  172. package/src/primitives/tabs.stories.tsx +180 -0
  173. package/src/primitives/tabs.tsx +71 -0
  174. package/src/primitives/terminal-display.stories.tsx +191 -0
  175. package/src/primitives/terminal-display.tsx +189 -0
  176. package/src/primitives/theme-toggle.stories.tsx +32 -0
  177. package/src/primitives/theme-toggle.tsx +96 -0
  178. package/src/primitives/toast.stories.tsx +155 -0
  179. package/src/primitives/toast.tsx +190 -0
  180. package/src/primitives/upload-progress.stories.tsx +120 -0
  181. package/src/primitives/upload-progress.tsx +110 -0
  182. package/src/run/expanded-tool-detail.stories.tsx +182 -0
  183. package/src/run/expanded-tool-detail.tsx +186 -0
  184. package/src/run/index.ts +13 -0
  185. package/src/run/inline-thinking-item.stories.tsx +136 -0
  186. package/src/run/inline-thinking-item.tsx +120 -0
  187. package/src/run/inline-tool-item.stories.tsx +222 -0
  188. package/src/run/inline-tool-item.tsx +190 -0
  189. package/src/run/run-group.stories.tsx +322 -0
  190. package/src/run/run-group.tsx +569 -0
  191. package/src/run/run-item-primitives.tsx +17 -0
  192. package/src/run/tool-call-feed.stories.tsx +294 -0
  193. package/src/run/tool-call-feed.tsx +192 -0
  194. package/src/run/tool-call-step.stories.tsx +198 -0
  195. package/src/run/tool-call-step.tsx +240 -0
  196. package/src/sdk-hooks.ts +38 -0
  197. package/src/stores/active-sessions-store.ts +455 -0
  198. package/src/stores/chat-store.ts +43 -0
  199. package/src/stores/index.ts +2 -0
  200. package/src/tool-previews/command-preview.tsx +116 -0
  201. package/src/tool-previews/diff-preview.tsx +85 -0
  202. package/src/tool-previews/glob-results-preview.tsx +98 -0
  203. package/src/tool-previews/grep-results-preview.tsx +157 -0
  204. package/src/tool-previews/index.ts +22 -0
  205. package/src/tool-previews/preview-primitives.tsx +84 -0
  206. package/src/tool-previews/question-preview.tsx +101 -0
  207. package/src/tool-previews/web-search-preview.tsx +117 -0
  208. package/src/tool-previews/write-file-preview.tsx +80 -0
  209. package/src/types/branding.ts +11 -0
  210. package/src/types/index.ts +5 -0
  211. package/src/types/message.ts +13 -0
  212. package/src/types/parts.ts +51 -0
  213. package/src/types/run.ts +56 -0
  214. package/src/types/tool-display.ts +41 -0
  215. package/src/utils/copy-text.ts +30 -0
  216. package/src/utils/format.test.ts +43 -0
  217. package/src/utils/format.ts +56 -0
  218. package/src/utils/index.ts +10 -0
  219. package/src/utils/time-ago.ts +9 -0
  220. package/src/utils/tool-display.ts +238 -0
@@ -0,0 +1,95 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Avatar, AvatarImage, AvatarFallback } from './avatar'
3
+
4
+ const meta: Meta<typeof Avatar> = {
5
+ title: 'Primitives/Avatar',
6
+ component: Avatar,
7
+ parameters: { layout: 'centered', backgrounds: { default: 'dark' } },
8
+ }
9
+
10
+ export default meta
11
+ type Story = StoryObj<typeof Avatar>
12
+
13
+ export const WithImage: Story = {
14
+ render: () => (
15
+ <Avatar>
16
+ <AvatarImage src="https://github.com/shadcn.png" alt="shadcn" />
17
+ <AvatarFallback>SC</AvatarFallback>
18
+ </Avatar>
19
+ ),
20
+ }
21
+
22
+ export const Fallback: Story = {
23
+ render: () => (
24
+ <Avatar>
25
+ <AvatarImage src="/broken.png" alt="Broken" />
26
+ <AvatarFallback>JD</AvatarFallback>
27
+ </Avatar>
28
+ ),
29
+ }
30
+
31
+ export const FallbackOnly: Story = {
32
+ render: () => (
33
+ <Avatar>
34
+ <AvatarFallback>AK</AvatarFallback>
35
+ </Avatar>
36
+ ),
37
+ }
38
+
39
+ export const CustomSize: Story = {
40
+ render: () => (
41
+ <Avatar className="h-16 w-16">
42
+ <AvatarFallback className="text-lg">BL</AvatarFallback>
43
+ </Avatar>
44
+ ),
45
+ }
46
+
47
+ export const Overview: Story = {
48
+ name: 'Overview',
49
+ render: () => (
50
+ <div className="flex flex-col gap-6 p-6">
51
+ <div className="text-muted-foreground text-xs font-mono uppercase tracking-widest">Sizes</div>
52
+ <div className="flex items-center gap-4">
53
+ <Avatar className="h-6 w-6">
54
+ <AvatarFallback className="text-xs">XS</AvatarFallback>
55
+ </Avatar>
56
+ <Avatar className="h-8 w-8">
57
+ <AvatarFallback className="text-xs">SM</AvatarFallback>
58
+ </Avatar>
59
+ <Avatar>
60
+ <AvatarFallback>MD</AvatarFallback>
61
+ </Avatar>
62
+ <Avatar className="h-14 w-14">
63
+ <AvatarFallback>LG</AvatarFallback>
64
+ </Avatar>
65
+ <Avatar className="h-20 w-20">
66
+ <AvatarFallback className="text-xl">XL</AvatarFallback>
67
+ </Avatar>
68
+ </div>
69
+ <div className="text-muted-foreground text-xs font-mono uppercase tracking-widest mt-2">Team</div>
70
+ <div className="flex items-center gap-3">
71
+ {[
72
+ { initials: 'AK', label: 'Alex Kim' },
73
+ { initials: 'JD', label: 'Jane Doe' },
74
+ { initials: 'MR', label: 'Marco Rossi' },
75
+ { initials: 'SP', label: 'Sara Park' },
76
+ ].map(({ initials, label }) => (
77
+ <div key={initials} className="flex flex-col items-center gap-1">
78
+ <Avatar>
79
+ <AvatarFallback>{initials}</AvatarFallback>
80
+ </Avatar>
81
+ <span className="text-muted-foreground text-xs">{label}</span>
82
+ </div>
83
+ ))}
84
+ </div>
85
+ <div className="text-muted-foreground text-xs font-mono uppercase tracking-widest mt-2">Stacked</div>
86
+ <div className="flex -space-x-2">
87
+ {['AK', 'JD', 'MR', 'SP', '+4'].map((label) => (
88
+ <Avatar key={label} className="border-2 border-background">
89
+ <AvatarFallback className="text-xs">{label}</AvatarFallback>
90
+ </Avatar>
91
+ ))}
92
+ </div>
93
+ </div>
94
+ ),
95
+ }
@@ -0,0 +1,47 @@
1
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/utils";
4
+
5
+ const Avatar = React.forwardRef<
6
+ React.ElementRef<typeof AvatarPrimitive.Root>,
7
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
8
+ >(({ className, ...props }, ref) => (
9
+ <AvatarPrimitive.Root
10
+ ref={ref}
11
+ className={cn(
12
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
13
+ className,
14
+ )}
15
+ {...props}
16
+ />
17
+ ));
18
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
19
+
20
+ const AvatarImage = React.forwardRef<
21
+ React.ElementRef<typeof AvatarPrimitive.Image>,
22
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
23
+ >(({ className, ...props }, ref) => (
24
+ <AvatarPrimitive.Image
25
+ ref={ref}
26
+ className={cn("aspect-square h-full w-full", className)}
27
+ {...props}
28
+ />
29
+ ));
30
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
31
+
32
+ const AvatarFallback = React.forwardRef<
33
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
34
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
35
+ >(({ className, ...props }, ref) => (
36
+ <AvatarPrimitive.Fallback
37
+ ref={ref}
38
+ className={cn(
39
+ "flex h-full w-full items-center justify-center rounded-full bg-muted font-medium text-sm",
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ ));
45
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
46
+
47
+ export { Avatar, AvatarImage, AvatarFallback };
@@ -0,0 +1,57 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Badge } from './badge'
3
+
4
+ const meta: Meta<typeof Badge> = {
5
+ title: 'Primitives/Badge',
6
+ component: Badge,
7
+ parameters: { layout: 'centered' },
8
+ args: { children: 'Label' },
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj<typeof Badge>
13
+
14
+ export const Default: Story = { args: { variant: 'default' } }
15
+ export const Secondary: Story = { args: { variant: 'secondary', children: 'Secondary' } }
16
+ export const Outline: Story = { args: { variant: 'outline', children: 'Outline' } }
17
+ export const Sandbox: Story = { args: { variant: 'sandbox', children: 'Sandbox' } }
18
+ export const Success: Story = { args: { variant: 'success', children: 'Success' } }
19
+ export const Warning: Story = { args: { variant: 'warning', children: 'Warning' } }
20
+ export const Error: Story = { args: { variant: 'error', children: 'Error' } }
21
+ export const Info: Story = { args: { variant: 'info', children: 'Info' } }
22
+
23
+ // Status variants
24
+ export const Running: Story = { args: { variant: 'running', dot: true, children: 'Running' } }
25
+ export const Creating: Story = { args: { variant: 'creating', dot: true, children: 'Creating' } }
26
+ export const Stopped: Story = { args: { variant: 'stopped', dot: true, children: 'Stopped' } }
27
+ export const Warm: Story = { args: { variant: 'warm', dot: true, children: 'Warm' } }
28
+ export const Cold: Story = { args: { variant: 'cold', dot: true, children: 'Cold' } }
29
+ export const Deleted: Story = { args: { variant: 'deleted', dot: true, children: 'Deleted' } }
30
+
31
+ export const AllVariants: Story = {
32
+ name: 'All Variants',
33
+ render: () => (
34
+ <div className="flex flex-col gap-4 p-6">
35
+ <div className="text-muted-foreground text-xs font-mono uppercase tracking-widest">Semantic</div>
36
+ <div className="flex flex-wrap gap-2">
37
+ <Badge variant="default">Default</Badge>
38
+ <Badge variant="secondary">Secondary</Badge>
39
+ <Badge variant="outline">Outline</Badge>
40
+ <Badge variant="sandbox">Sandbox</Badge>
41
+ <Badge variant="success">Success</Badge>
42
+ <Badge variant="warning">Warning</Badge>
43
+ <Badge variant="error">Error</Badge>
44
+ <Badge variant="info">Info</Badge>
45
+ </div>
46
+ <div className="text-muted-foreground text-xs font-mono uppercase tracking-widest mt-2">Operational Status</div>
47
+ <div className="flex flex-wrap gap-2">
48
+ <Badge variant="running" dot>Running</Badge>
49
+ <Badge variant="creating" dot>Creating</Badge>
50
+ <Badge variant="stopped" dot>Stopped</Badge>
51
+ <Badge variant="warm" dot>Warm</Badge>
52
+ <Badge variant="cold" dot>Cold</Badge>
53
+ <Badge variant="deleted" dot>Deleted</Badge>
54
+ </div>
55
+ </div>
56
+ ),
57
+ }
@@ -0,0 +1,97 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import type * as React from "react";
3
+ import { cn } from "../lib/utils";
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 font-medium text-xs transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "border-transparent bg-primary text-primary-foreground",
11
+ secondary: "border-transparent bg-secondary text-secondary-foreground",
12
+ destructive: "border-transparent bg-destructive/15 border-destructive/25 text-destructive",
13
+ outline: "text-foreground border-border",
14
+ success:
15
+ "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
16
+ warning:
17
+ "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
18
+ error:
19
+ "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
20
+ info:
21
+ "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
22
+ sandbox:
23
+ "border-border bg-[var(--accent-surface-soft)] text-[var(--accent-text)]",
24
+ /* Operational status variants */
25
+ running:
26
+ "border-[var(--surface-teal-border)] bg-[var(--surface-teal-bg)] text-[var(--surface-teal-text)]",
27
+ creating:
28
+ "border-[var(--surface-violet-border)] bg-[var(--surface-violet-bg)] text-[var(--surface-violet-text)]",
29
+ stopped:
30
+ "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
31
+ warm:
32
+ "border-[var(--surface-orange-border)] bg-[var(--surface-orange-bg)] text-[var(--surface-orange-text)]",
33
+ cold:
34
+ "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
35
+ deleted:
36
+ "border-[var(--surface-neutral-border)] bg-[var(--surface-neutral-bg)] text-[var(--surface-neutral-text)]",
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ variant: "default",
41
+ },
42
+ },
43
+ );
44
+
45
+ export type BadgeVariant =
46
+ | "default"
47
+ | "secondary"
48
+ | "destructive"
49
+ | "outline"
50
+ | "success"
51
+ | "warning"
52
+ | "error"
53
+ | "info"
54
+ | "sandbox"
55
+ | "running"
56
+ | "creating"
57
+ | "stopped"
58
+ | "warm"
59
+ | "cold"
60
+ | "deleted"
61
+ | null;
62
+
63
+ export interface BadgeProps extends VariantProps<typeof badgeVariants> {
64
+ variant?: BadgeVariant;
65
+ className?: string;
66
+ children?: React.ReactNode;
67
+ dot?: boolean;
68
+ onClick?: React.MouseEventHandler<HTMLDivElement>;
69
+ style?: React.CSSProperties;
70
+ id?: string;
71
+ role?: React.AriaRole;
72
+ title?: string;
73
+ [key: string]: unknown;
74
+ }
75
+
76
+ const statusDotClass: Record<string, string> = {
77
+ running: "status-dot status-dot-running",
78
+ creating: "status-dot status-dot-creating",
79
+ stopped: "status-dot status-dot-stopped",
80
+ warm: "status-dot status-dot-warm",
81
+ cold: "status-dot status-dot-cold",
82
+ error: "status-dot status-dot-error",
83
+ deleted: "status-dot status-dot-deleted",
84
+ };
85
+
86
+ function Badge({ className, variant, dot, children, ...props }: BadgeProps) {
87
+ const dotClass = dot && variant ? statusDotClass[variant] : null;
88
+
89
+ return (
90
+ <div className={cn(badgeVariants({ variant }), className)} {...props}>
91
+ {dotClass && <span className={dotClass} aria-hidden />}
92
+ {children}
93
+ </div>
94
+ );
95
+ }
96
+
97
+ export { Badge, badgeVariants };
@@ -0,0 +1,48 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Button } from './button'
3
+
4
+ const meta: Meta<typeof Button> = {
5
+ title: 'Primitives/Button',
6
+ component: Button,
7
+ parameters: { layout: 'centered' },
8
+ args: { children: 'Button' },
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj<typeof Button>
13
+
14
+ export const Default: Story = {}
15
+ export const Sandbox: Story = { args: { variant: 'sandbox', children: 'Get Started' } }
16
+ export const Secondary: Story = { args: { variant: 'secondary' } }
17
+ export const Outline: Story = { args: { variant: 'outline' } }
18
+ export const Ghost: Story = { args: { variant: 'ghost' } }
19
+ export const Destructive: Story = { args: { variant: 'destructive', children: 'Delete' } }
20
+ export const Loading: Story = { args: { loading: true, children: 'Loading...' } }
21
+
22
+ export const AllSizes: Story = {
23
+ name: 'All Sizes',
24
+ render: () => (
25
+ <div className="flex items-center gap-3">
26
+ <Button size="sm">Small</Button>
27
+ <Button>Default</Button>
28
+ <Button size="lg">Large</Button>
29
+ <Button size="xl">XLarge</Button>
30
+ </div>
31
+ ),
32
+ }
33
+
34
+ export const AllVariants: Story = {
35
+ name: 'All Variants',
36
+ render: () => (
37
+ <div className="flex flex-col gap-4 p-6">
38
+ <div className="flex flex-wrap gap-2">
39
+ <Button variant="default">Default</Button>
40
+ <Button variant="sandbox">Sandbox</Button>
41
+ <Button variant="secondary">Secondary</Button>
42
+ <Button variant="outline">Outline</Button>
43
+ <Button variant="ghost">Ghost</Button>
44
+ <Button variant="destructive">Destructive</Button>
45
+ </div>
46
+ </div>
47
+ ),
48
+ }
@@ -0,0 +1,115 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium text-sm transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97] duration-[var(--transition-fast)]",
13
+ destructive:
14
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.97] duration-[var(--transition-fast)]",
15
+ outline:
16
+ "border border-border bg-card hover:bg-muted active:scale-[0.97] duration-[var(--transition-fast)] text-foreground",
17
+ secondary:
18
+ "bg-muted border border-border text-foreground hover:bg-muted/80 active:scale-[0.97] duration-[var(--transition-fast)]",
19
+ ghost:
20
+ "hover:bg-muted hover:text-foreground duration-[var(--transition-fast)] text-muted-foreground border border-transparent",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ sandbox:
23
+ "bg-[var(--btn-primary-bg)] text-[var(--btn-primary-text)] border border-[var(--border-accent)] hover:bg-[var(--btn-primary-hover)] active:scale-[0.97] duration-[var(--transition-fast)]",
24
+ },
25
+ size: {
26
+ default: "h-[var(--control-height)] px-4 py-2",
27
+ sm: "h-8 rounded-md px-3 text-xs",
28
+ lg: "h-11 rounded-lg px-7 text-sm",
29
+ xl: "h-13 rounded-xl px-9 text-base",
30
+ icon: "h-[var(--control-height)] w-[var(--control-height)]",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: "default",
35
+ size: "default",
36
+ },
37
+ },
38
+ );
39
+
40
+ export interface ButtonProps
41
+ extends React.ButtonHTMLAttributes<HTMLButtonElement> {
42
+ variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "sandbox" | null;
43
+ size?: "default" | "sm" | "lg" | "xl" | "icon" | null;
44
+ asChild?: boolean;
45
+ loading?: boolean;
46
+ children?: React.ReactNode;
47
+ }
48
+
49
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
50
+ (
51
+ {
52
+ className,
53
+ variant,
54
+ size,
55
+ asChild = false,
56
+ loading = false,
57
+ disabled,
58
+ children,
59
+ ...props
60
+ },
61
+ ref,
62
+ ) => {
63
+ const Comp = asChild ? Slot : "button";
64
+
65
+ // When using asChild, we can't add the loading spinner as it would create multiple children
66
+ // which breaks Slot's single-child requirement
67
+ if (asChild) {
68
+ return (
69
+ <Comp
70
+ className={cn(buttonVariants({ variant, size, className }))}
71
+ ref={ref}
72
+ {...props}
73
+ >
74
+ {children}
75
+ </Comp>
76
+ );
77
+ }
78
+
79
+ return (
80
+ <Comp
81
+ className={cn(buttonVariants({ variant, size, className }))}
82
+ ref={ref}
83
+ disabled={disabled || loading}
84
+ {...props}
85
+ >
86
+ {loading && (
87
+ <svg
88
+ className="mr-2 -ml-1 h-4 w-4 animate-spin"
89
+ fill="none"
90
+ viewBox="0 0 24 24"
91
+ >
92
+ <title>Loading spinner</title>
93
+ <circle
94
+ className="opacity-25"
95
+ cx="12"
96
+ cy="12"
97
+ r="10"
98
+ stroke="currentColor"
99
+ strokeWidth="4"
100
+ />
101
+ <path
102
+ className="opacity-75"
103
+ fill="currentColor"
104
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
105
+ />
106
+ </svg>
107
+ )}
108
+ {children}
109
+ </Comp>
110
+ );
111
+ },
112
+ );
113
+ Button.displayName = "Button";
114
+
115
+ export { Button, buttonVariants };
@@ -0,0 +1,53 @@
1
+ import type { Meta, StoryObj } from '@storybook/react'
2
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './card'
3
+ import { Badge } from './badge'
4
+
5
+ const meta: Meta<typeof Card> = {
6
+ title: 'Primitives/Card',
7
+ component: Card,
8
+ parameters: { layout: 'centered' },
9
+ }
10
+
11
+ export default meta
12
+ type Story = StoryObj<typeof Card>
13
+
14
+ const SampleCard = ({ variant, hover }: { variant?: 'default' | 'glass' | 'sandbox' | 'elevated', hover?: boolean }) => (
15
+ <Card variant={variant} hover={hover} className="w-72">
16
+ <CardHeader>
17
+ <div className="flex items-center justify-between">
18
+ <CardTitle>Agent Session</CardTitle>
19
+ <Badge variant="running" dot>Running</Badge>
20
+ </div>
21
+ <CardDescription>GPT-4o · Tangle Cloud</CardDescription>
22
+ </CardHeader>
23
+ <CardContent>
24
+ <p className="text-sm text-muted-foreground">
25
+ Processing 14 tool calls · 3.2k tokens used
26
+ </p>
27
+ </CardContent>
28
+ </Card>
29
+ )
30
+
31
+ export const Default: Story = { render: () => <SampleCard variant="default" /> }
32
+ export const Elevated: Story = { render: () => <SampleCard variant="elevated" /> }
33
+ export const Glass: Story = {
34
+ render: () => (
35
+ <div className="bg-mesh bg-background p-10 rounded-xl">
36
+ <SampleCard variant="glass" />
37
+ </div>
38
+ ),
39
+ }
40
+ export const Sandbox: Story = { render: () => <SampleCard variant="sandbox" /> }
41
+ export const Hoverable: Story = { render: () => <SampleCard variant="default" hover /> }
42
+
43
+ export const AllVariants: Story = {
44
+ name: 'All Variants',
45
+ render: () => (
46
+ <div className="bg-mesh bg-background p-10 rounded-xl flex flex-wrap gap-4">
47
+ <SampleCard variant="default" />
48
+ <SampleCard variant="elevated" />
49
+ <SampleCard variant="glass" />
50
+ <SampleCard variant="sandbox" />
51
+ </div>
52
+ ),
53
+ }
@@ -0,0 +1,98 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+
4
+ const Card = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement> & {
7
+ variant?: "default" | "glass" | "sandbox" | "elevated";
8
+ hover?: boolean;
9
+ }
10
+ >(({ className, variant = "default", hover = false, ...props }, ref) => {
11
+ const variants = {
12
+ default: "bg-card border-border",
13
+ elevated: "bg-muted/50 border-border shadow-[var(--shadow-card)]",
14
+ glass: "bg-card/80 backdrop-blur-xl border-border shadow-[var(--shadow-card)]",
15
+ sandbox: "bg-muted/50 border-primary/20 shadow-[var(--shadow-accent)]",
16
+ };
17
+
18
+ return (
19
+ <div
20
+ ref={ref}
21
+ className={cn(
22
+ "rounded-[var(--radius-lg)] border text-card-foreground transition-[border-color,box-shadow]",
23
+ "duration-[var(--transition-default)]",
24
+ variants[variant],
25
+ hover &&
26
+ "cursor-pointer hover:border-primary/30",
27
+ className,
28
+ )}
29
+ {...props}
30
+ />
31
+ );
32
+ });
33
+ Card.displayName = "Card";
34
+
35
+ const CardHeader = React.forwardRef<
36
+ HTMLDivElement,
37
+ React.HTMLAttributes<HTMLDivElement>
38
+ >(({ className, ...props }, ref) => (
39
+ <div
40
+ ref={ref}
41
+ className={cn("flex flex-col space-y-1 p-4", className)}
42
+ {...props}
43
+ />
44
+ ));
45
+ CardHeader.displayName = "CardHeader";
46
+
47
+ const CardTitle = React.forwardRef<
48
+ HTMLHeadingElement,
49
+ React.HTMLAttributes<HTMLHeadingElement>
50
+ >(({ className, ...props }, ref) => (
51
+ <h3
52
+ ref={ref}
53
+ className={cn("font-semibold leading-none tracking-tight", className)}
54
+ {...props}
55
+ />
56
+ ));
57
+ CardTitle.displayName = "CardTitle";
58
+
59
+ const CardDescription = React.forwardRef<
60
+ HTMLParagraphElement,
61
+ React.HTMLAttributes<HTMLParagraphElement>
62
+ >(({ className, ...props }, ref) => (
63
+ <p
64
+ ref={ref}
65
+ className={cn("text-muted-foreground text-sm", className)}
66
+ {...props}
67
+ />
68
+ ));
69
+ CardDescription.displayName = "CardDescription";
70
+
71
+ const CardContent = React.forwardRef<
72
+ HTMLDivElement,
73
+ React.HTMLAttributes<HTMLDivElement>
74
+ >(({ className, ...props }, ref) => (
75
+ <div ref={ref} className={cn("p-4 pt-0", className)} {...props} />
76
+ ));
77
+ CardContent.displayName = "CardContent";
78
+
79
+ const CardFooter = React.forwardRef<
80
+ HTMLDivElement,
81
+ React.HTMLAttributes<HTMLDivElement>
82
+ >(({ className, ...props }, ref) => (
83
+ <div
84
+ ref={ref}
85
+ className={cn("flex items-center p-6 pt-0", className)}
86
+ {...props}
87
+ />
88
+ ));
89
+ CardFooter.displayName = "CardFooter";
90
+
91
+ export {
92
+ Card,
93
+ CardHeader,
94
+ CardFooter,
95
+ CardTitle,
96
+ CardDescription,
97
+ CardContent,
98
+ };