@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
File without changes
@@ -0,0 +1,109 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/primitives/button.tsx
6
+ import { Slot } from "@radix-ui/react-slot";
7
+ import { cva } from "class-variance-authority";
8
+ import * as React from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ var buttonVariants = cva(
11
+ "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",
12
+ {
13
+ variants: {
14
+ variant: {
15
+ default: "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97] duration-[var(--transition-fast)]",
16
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.97] duration-[var(--transition-fast)]",
17
+ outline: "border border-border bg-card hover:bg-muted active:scale-[0.97] duration-[var(--transition-fast)] text-foreground",
18
+ secondary: "bg-muted border border-border text-foreground hover:bg-muted/80 active:scale-[0.97] duration-[var(--transition-fast)]",
19
+ ghost: "hover:bg-muted hover:text-foreground duration-[var(--transition-fast)] text-muted-foreground border border-transparent",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ sandbox: "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)]"
22
+ },
23
+ size: {
24
+ default: "h-[var(--control-height)] px-4 py-2",
25
+ sm: "h-8 rounded-md px-3 text-xs",
26
+ lg: "h-11 rounded-lg px-7 text-sm",
27
+ xl: "h-13 rounded-xl px-9 text-base",
28
+ icon: "h-[var(--control-height)] w-[var(--control-height)]"
29
+ }
30
+ },
31
+ defaultVariants: {
32
+ variant: "default",
33
+ size: "default"
34
+ }
35
+ }
36
+ );
37
+ var Button = React.forwardRef(
38
+ ({
39
+ className,
40
+ variant,
41
+ size,
42
+ asChild = false,
43
+ loading = false,
44
+ disabled,
45
+ children,
46
+ ...props
47
+ }, ref) => {
48
+ const Comp = asChild ? Slot : "button";
49
+ if (asChild) {
50
+ return /* @__PURE__ */ jsx(
51
+ Comp,
52
+ {
53
+ className: cn(buttonVariants({ variant, size, className })),
54
+ ref,
55
+ ...props,
56
+ children
57
+ }
58
+ );
59
+ }
60
+ return /* @__PURE__ */ jsxs(
61
+ Comp,
62
+ {
63
+ className: cn(buttonVariants({ variant, size, className })),
64
+ ref,
65
+ disabled: disabled || loading,
66
+ ...props,
67
+ children: [
68
+ loading && /* @__PURE__ */ jsxs(
69
+ "svg",
70
+ {
71
+ className: "mr-2 -ml-1 h-4 w-4 animate-spin",
72
+ fill: "none",
73
+ viewBox: "0 0 24 24",
74
+ children: [
75
+ /* @__PURE__ */ jsx("title", { children: "Loading spinner" }),
76
+ /* @__PURE__ */ jsx(
77
+ "circle",
78
+ {
79
+ className: "opacity-25",
80
+ cx: "12",
81
+ cy: "12",
82
+ r: "10",
83
+ stroke: "currentColor",
84
+ strokeWidth: "4"
85
+ }
86
+ ),
87
+ /* @__PURE__ */ jsx(
88
+ "path",
89
+ {
90
+ className: "opacity-75",
91
+ fill: "currentColor",
92
+ 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"
93
+ }
94
+ )
95
+ ]
96
+ }
97
+ ),
98
+ children
99
+ ]
100
+ }
101
+ );
102
+ }
103
+ );
104
+ Button.displayName = "Button";
105
+
106
+ export {
107
+ buttonVariants,
108
+ Button
109
+ };
@@ -0,0 +1,192 @@
1
+ import {
2
+ Avatar,
3
+ AvatarFallback,
4
+ AvatarImage,
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuItem,
8
+ DropdownMenuLabel,
9
+ DropdownMenuSeparator,
10
+ DropdownMenuTrigger
11
+ } from "./chunk-34A66VBG.js";
12
+ import {
13
+ Button
14
+ } from "./chunk-MKTSMWVD.js";
15
+ import {
16
+ cn
17
+ } from "./chunk-RQHJBTEU.js";
18
+
19
+ // src/auth/auth.tsx
20
+ import { jsx, jsxs } from "react/jsx-runtime";
21
+ function GitHubIcon({ className }) {
22
+ return /* @__PURE__ */ jsx(
23
+ "svg",
24
+ {
25
+ className,
26
+ viewBox: "0 0 24 24",
27
+ fill: "currentColor",
28
+ "aria-hidden": "true",
29
+ children: /* @__PURE__ */ jsx(
30
+ "path",
31
+ {
32
+ fillRule: "evenodd",
33
+ clipRule: "evenodd",
34
+ d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
35
+ }
36
+ )
37
+ }
38
+ );
39
+ }
40
+ function GitHubLoginButton({
41
+ authUrl = "/auth/github",
42
+ variant = "default",
43
+ className,
44
+ children,
45
+ ...props
46
+ }) {
47
+ return /* @__PURE__ */ jsxs(
48
+ Button,
49
+ {
50
+ variant,
51
+ className: cn("gap-2", className),
52
+ onClick: () => {
53
+ window.location.href = authUrl;
54
+ },
55
+ ...props,
56
+ children: [
57
+ /* @__PURE__ */ jsx(GitHubIcon, { className: "h-5 w-5" }),
58
+ children ?? "Sign in with GitHub"
59
+ ]
60
+ }
61
+ );
62
+ }
63
+ function UserMenu({
64
+ user,
65
+ logoutUrl = "/auth/logout",
66
+ links = [],
67
+ variant = "sandbox",
68
+ onLogout
69
+ }) {
70
+ const initials = user.name ? user.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : user.email.slice(0, 2).toUpperCase();
71
+ const avatarGradient = {
72
+ sandbox: "bg-[image:var(--accent-gradient-strong)]"
73
+ }[variant];
74
+ const handleLogout = () => {
75
+ if (onLogout) {
76
+ onLogout();
77
+ }
78
+ window.location.href = logoutUrl;
79
+ };
80
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
81
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ className: "flex items-center gap-2 rounded-lg p-1.5 transition-colors hover:bg-accent focus:outline-none focus:ring-2 focus:ring-ring",
86
+ children: [
87
+ /* @__PURE__ */ jsxs(Avatar, { className: "h-8 w-8", children: [
88
+ /* @__PURE__ */ jsx(AvatarImage, { src: void 0, alt: user.name ?? user.email }),
89
+ /* @__PURE__ */ jsx(
90
+ AvatarFallback,
91
+ {
92
+ className: cn(
93
+ "text-white text-xs",
94
+ avatarGradient
95
+ ),
96
+ children: initials
97
+ }
98
+ )
99
+ ] }),
100
+ /* @__PURE__ */ jsxs("div", { className: "hidden text-left md:block", children: [
101
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-sm", children: user.name ?? user.email }),
102
+ user.tier && /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-xs capitalize", children: [
103
+ user.tier,
104
+ " Plan"
105
+ ] })
106
+ ] })
107
+ ]
108
+ }
109
+ ) }),
110
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
111
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "font-normal", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col space-y-1", children: [
112
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-sm", children: user.name ?? "Account" }),
113
+ /* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-xs", children: user.email })
114
+ ] }) }),
115
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
116
+ links.map((link) => /* @__PURE__ */ jsx(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs("a", { href: link.href, className: "flex items-center gap-2", children: [
117
+ link.icon,
118
+ link.label
119
+ ] }) }, link.href)),
120
+ links.length > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
121
+ /* @__PURE__ */ jsx(
122
+ DropdownMenuItem,
123
+ {
124
+ onClick: handleLogout,
125
+ className: "text-[var(--surface-danger-text)] focus:text-[var(--surface-danger-text)]",
126
+ children: "Sign out"
127
+ }
128
+ )
129
+ ] })
130
+ ] });
131
+ }
132
+ function AuthHeader({
133
+ user,
134
+ loading = false,
135
+ variant = "sandbox",
136
+ apiBaseUrl = "",
137
+ menuLinks,
138
+ className
139
+ }) {
140
+ if (loading) {
141
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", className), children: [
142
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-pulse rounded-full bg-muted" }),
143
+ /* @__PURE__ */ jsx("div", { className: "hidden md:block", children: /* @__PURE__ */ jsx("div", { className: "h-4 w-20 animate-pulse rounded bg-muted" }) })
144
+ ] });
145
+ }
146
+ if (!user) {
147
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", className), children: /* @__PURE__ */ jsx(
148
+ GitHubLoginButton,
149
+ {
150
+ authUrl: `${apiBaseUrl}/auth/github`,
151
+ variant,
152
+ size: "sm"
153
+ }
154
+ ) });
155
+ }
156
+ return /* @__PURE__ */ jsx(
157
+ UserMenu,
158
+ {
159
+ user,
160
+ variant,
161
+ logoutUrl: `${apiBaseUrl}/auth/logout`,
162
+ links: menuLinks
163
+ }
164
+ );
165
+ }
166
+
167
+ // src/auth/login-layout.tsx
168
+ import { Terminal } from "lucide-react";
169
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
170
+ function LoginLayout({
171
+ title = "Welcome Back",
172
+ subtitle = "Sign in to your workspace.",
173
+ brandIcon,
174
+ children,
175
+ className
176
+ }) {
177
+ return /* @__PURE__ */ jsx2("div", { className: cn("relative flex min-h-screen items-center justify-center bg-background overflow-hidden antialiased font-sans flex-col", className), children: /* @__PURE__ */ jsxs2("div", { className: "z-10 w-full max-w-md px-6 animate-in flex flex-col items-center", children: [
178
+ /* @__PURE__ */ jsxs2("div", { className: "mb-10 text-center flex flex-col items-center", children: [
179
+ /* @__PURE__ */ jsx2("div", { className: "inline-flex h-14 w-14 mb-4 items-center justify-center rounded-lg bg-muted border border-border", children: brandIcon || /* @__PURE__ */ jsx2(Terminal, { className: "h-7 w-7 text-foreground" }) }),
180
+ /* @__PURE__ */ jsx2("h1", { className: "text-2xl font-bold tracking-tight text-foreground font-display", children: title }),
181
+ /* @__PURE__ */ jsx2("p", { className: "mt-2 text-sm text-muted-foreground", children: subtitle })
182
+ ] }),
183
+ /* @__PURE__ */ jsx2("div", { className: "w-full bg-card p-8 border border-border rounded-lg", children })
184
+ ] }) });
185
+ }
186
+
187
+ export {
188
+ GitHubLoginButton,
189
+ UserMenu,
190
+ AuthHeader,
191
+ LoginLayout
192
+ };
@@ -0,0 +1,321 @@
1
+ // src/stores/active-sessions-store.ts
2
+ import { useStore } from "@nanostores/react";
3
+ import { atom } from "nanostores";
4
+ import { useMemo } from "react";
5
+ var INITIAL_STATE = {
6
+ sessions: {},
7
+ lastUpdatedAt: 0
8
+ };
9
+ var STATUS_PRIORITY = {
10
+ running: 0,
11
+ error: 1,
12
+ "attention-needed": 2,
13
+ idle: 3
14
+ };
15
+ var DEFAULT_RECORD = {
16
+ status: "idle",
17
+ isRunning: false,
18
+ isForeground: false,
19
+ needsAttention: false,
20
+ connectionState: "disconnected",
21
+ reconnectState: "idle",
22
+ transportMode: null,
23
+ lastError: null,
24
+ lastEventAt: null
25
+ };
26
+ var activeSessionsAtom = atom(INITIAL_STATE);
27
+ var foregroundSessionId = null;
28
+ function createRecord(options, now) {
29
+ return {
30
+ sessionId: options.sessionId,
31
+ projectId: options.projectId ?? null,
32
+ projectLabel: options.projectLabel,
33
+ title: options.title,
34
+ href: options.href,
35
+ registeredAt: now,
36
+ lastActivityAt: now,
37
+ metadata: options.metadata,
38
+ ...DEFAULT_RECORD,
39
+ isForeground: options.sessionId === foregroundSessionId
40
+ };
41
+ }
42
+ function resolveStatus(session) {
43
+ if (session.isRunning) return "running";
44
+ if (session.lastError || session.reconnectState === "failed") return "error";
45
+ if (session.needsAttention) return "attention-needed";
46
+ return "idle";
47
+ }
48
+ function normalizeSession(session) {
49
+ return {
50
+ ...session,
51
+ status: resolveStatus(session)
52
+ };
53
+ }
54
+ function updateSession(sessionId, updater) {
55
+ const current = activeSessionsAtom.get();
56
+ const existing = current.sessions[sessionId];
57
+ if (!existing) return;
58
+ const now = Date.now();
59
+ activeSessionsAtom.set({
60
+ sessions: {
61
+ ...current.sessions,
62
+ [sessionId]: normalizeSession(updater(existing, now))
63
+ },
64
+ lastUpdatedAt: now
65
+ });
66
+ }
67
+ function registerActiveSession(options) {
68
+ const now = Date.now();
69
+ const current = activeSessionsAtom.get();
70
+ const existing = current.sessions[options.sessionId];
71
+ activeSessionsAtom.set({
72
+ sessions: {
73
+ ...current.sessions,
74
+ [options.sessionId]: normalizeSession(
75
+ existing ? {
76
+ ...existing,
77
+ projectId: existing.projectId ?? options.projectId ?? null,
78
+ projectLabel: options.projectLabel ?? existing.projectLabel,
79
+ title: options.title ?? existing.title,
80
+ href: options.href ?? existing.href,
81
+ metadata: options.metadata ?? existing.metadata,
82
+ lastActivityAt: now,
83
+ isForeground: options.sessionId === foregroundSessionId || existing.isForeground
84
+ } : createRecord(options, now)
85
+ )
86
+ },
87
+ lastUpdatedAt: now
88
+ });
89
+ }
90
+ function unregisterActiveSession(sessionId) {
91
+ const current = activeSessionsAtom.get();
92
+ if (!current.sessions[sessionId]) return;
93
+ const { [sessionId]: _removed, ...remaining } = current.sessions;
94
+ if (foregroundSessionId === sessionId) {
95
+ foregroundSessionId = null;
96
+ }
97
+ activeSessionsAtom.set({
98
+ sessions: remaining,
99
+ lastUpdatedAt: Date.now()
100
+ });
101
+ }
102
+ function setForegroundActiveSession(sessionId) {
103
+ foregroundSessionId = sessionId;
104
+ const current = activeSessionsAtom.get();
105
+ const now = Date.now();
106
+ let changed = false;
107
+ const sessions = Object.fromEntries(
108
+ Object.entries(current.sessions).map(([id, session]) => {
109
+ const isForeground = id === sessionId;
110
+ if (session.isForeground !== isForeground) {
111
+ changed = true;
112
+ }
113
+ return [
114
+ id,
115
+ {
116
+ ...session,
117
+ isForeground,
118
+ lastActivityAt: isForeground ? now : session.lastActivityAt
119
+ }
120
+ ];
121
+ })
122
+ );
123
+ if (!changed) return;
124
+ activeSessionsAtom.set({
125
+ sessions,
126
+ lastUpdatedAt: now
127
+ });
128
+ }
129
+ function updateActiveSessionMeta(sessionId, meta) {
130
+ updateSession(sessionId, (session, now) => ({
131
+ ...session,
132
+ ...meta,
133
+ lastActivityAt: now
134
+ }));
135
+ }
136
+ function setActiveSessionConnection(sessionId, options) {
137
+ updateSession(sessionId, (session, now) => ({
138
+ ...session,
139
+ connectionState: options.connectionState,
140
+ reconnectState: options.reconnectState ?? session.reconnectState,
141
+ transportMode: options.transportMode ?? session.transportMode,
142
+ lastError: options.lastError === void 0 ? session.lastError : options.lastError,
143
+ lastEventAt: options.lastEventAt ?? session.lastEventAt,
144
+ lastActivityAt: now
145
+ }));
146
+ }
147
+ function setActiveSessionRunning(sessionId, isRunning, options) {
148
+ updateSession(sessionId, (session, now) => ({
149
+ ...session,
150
+ isRunning,
151
+ needsAttention: isRunning ? false : session.needsAttention,
152
+ lastError: isRunning ? null : session.lastError,
153
+ lastEventAt: options?.lastEventAt ?? session.lastEventAt,
154
+ lastActivityAt: now
155
+ }));
156
+ }
157
+ function setActiveSessionAttention(sessionId, needsAttention, options) {
158
+ updateSession(sessionId, (session, now) => ({
159
+ ...session,
160
+ needsAttention,
161
+ isRunning: needsAttention ? false : session.isRunning,
162
+ lastEventAt: options?.lastEventAt ?? session.lastEventAt,
163
+ lastActivityAt: now
164
+ }));
165
+ }
166
+ function setActiveSessionError(sessionId, error) {
167
+ updateSession(sessionId, (session, now) => ({
168
+ ...session,
169
+ isRunning: false,
170
+ needsAttention: false,
171
+ lastError: error,
172
+ reconnectState: error ? "failed" : "idle",
173
+ connectionState: error ? "error" : session.connectionState,
174
+ lastEventAt: now,
175
+ lastActivityAt: now
176
+ }));
177
+ }
178
+ function bumpActiveSessionActivity(sessionId, options) {
179
+ updateSession(sessionId, (session, now) => ({
180
+ ...session,
181
+ lastEventAt: options?.lastEventAt ?? session.lastEventAt ?? now,
182
+ lastActivityAt: now
183
+ }));
184
+ }
185
+ function resetActiveSessions() {
186
+ foregroundSessionId = null;
187
+ activeSessionsAtom.set(INITIAL_STATE);
188
+ }
189
+ function getAllActiveSessions(state) {
190
+ return Object.values(state.sessions);
191
+ }
192
+ function getActiveSession(state, sessionId) {
193
+ return state.sessions[sessionId] ?? null;
194
+ }
195
+ function getSessionsForProject(state, projectId) {
196
+ return Object.values(state.sessions).filter((session) => session.projectId === projectId);
197
+ }
198
+ function getSessionsForNavbar(state, projectId) {
199
+ const sessions = projectId == null ? Object.values(state.sessions) : getSessionsForProject(state, projectId);
200
+ return [...sessions].sort((left, right) => {
201
+ if (left.isForeground !== right.isForeground) {
202
+ return Number(right.isForeground) - Number(left.isForeground);
203
+ }
204
+ const statusDiff = STATUS_PRIORITY[left.status] - STATUS_PRIORITY[right.status];
205
+ if (statusDiff !== 0) {
206
+ return statusDiff;
207
+ }
208
+ return right.lastActivityAt - left.lastActivityAt;
209
+ });
210
+ }
211
+ function getSessionsByActivity(state) {
212
+ return [...Object.values(state.sessions)].sort(
213
+ (left, right) => right.lastActivityAt - left.lastActivityAt
214
+ );
215
+ }
216
+ function getTotalRunningSessionCount(state) {
217
+ return Object.values(state.sessions).filter((session) => session.isRunning).length;
218
+ }
219
+ function hasBackgroundRunningSessions(state) {
220
+ return Object.values(state.sessions).some(
221
+ (session) => session.isRunning && !session.isForeground
222
+ );
223
+ }
224
+ function getAllProjectActivity(state) {
225
+ const grouped = /* @__PURE__ */ new Map();
226
+ for (const session of Object.values(state.sessions)) {
227
+ if (session.projectId == null) continue;
228
+ const existing = grouped.get(session.projectId);
229
+ if (existing) {
230
+ existing.activeSessionCount += 1;
231
+ existing.lastActivityAt = Math.max(existing.lastActivityAt, session.lastActivityAt);
232
+ if (session.isRunning) {
233
+ existing.runningSessionIds.push(session.sessionId);
234
+ }
235
+ if (!existing.projectLabel && session.projectLabel) {
236
+ existing.projectLabel = session.projectLabel;
237
+ }
238
+ continue;
239
+ }
240
+ grouped.set(session.projectId, {
241
+ projectId: session.projectId,
242
+ projectLabel: session.projectLabel,
243
+ activeSessionCount: 1,
244
+ runningSessionIds: session.isRunning ? [session.sessionId] : [],
245
+ lastActivityAt: session.lastActivityAt
246
+ });
247
+ }
248
+ return [...grouped.values()].sort((left, right) => right.lastActivityAt - left.lastActivityAt);
249
+ }
250
+ function useActiveSessionsState() {
251
+ return useStore(activeSessionsAtom);
252
+ }
253
+ function useActiveSessions() {
254
+ const state = useStore(activeSessionsAtom);
255
+ return useMemo(() => getAllActiveSessions(state), [state]);
256
+ }
257
+ function useActiveSession(sessionId) {
258
+ const state = useStore(activeSessionsAtom);
259
+ return useMemo(
260
+ () => sessionId ? getActiveSession(state, sessionId) : null,
261
+ [sessionId, state]
262
+ );
263
+ }
264
+ function useProjectSessions(projectId) {
265
+ const state = useStore(activeSessionsAtom);
266
+ return useMemo(
267
+ () => projectId == null ? [] : getSessionsForProject(state, projectId),
268
+ [projectId, state]
269
+ );
270
+ }
271
+ function useNavbarSessions(projectId) {
272
+ const state = useStore(activeSessionsAtom);
273
+ return useMemo(() => getSessionsForNavbar(state, projectId), [projectId, state]);
274
+ }
275
+ function useSessionsByActivity() {
276
+ const state = useStore(activeSessionsAtom);
277
+ return useMemo(() => getSessionsByActivity(state), [state]);
278
+ }
279
+ function useProjectActivity() {
280
+ const state = useStore(activeSessionsAtom);
281
+ return useMemo(() => getAllProjectActivity(state), [state]);
282
+ }
283
+ function useTotalRunningSessions() {
284
+ const state = useStore(activeSessionsAtom);
285
+ return useMemo(() => getTotalRunningSessionCount(state), [state]);
286
+ }
287
+ function useHasBackgroundRunningSessions() {
288
+ const state = useStore(activeSessionsAtom);
289
+ return useMemo(() => hasBackgroundRunningSessions(state), [state]);
290
+ }
291
+
292
+ export {
293
+ activeSessionsAtom,
294
+ registerActiveSession,
295
+ unregisterActiveSession,
296
+ setForegroundActiveSession,
297
+ updateActiveSessionMeta,
298
+ setActiveSessionConnection,
299
+ setActiveSessionRunning,
300
+ setActiveSessionAttention,
301
+ setActiveSessionError,
302
+ bumpActiveSessionActivity,
303
+ resetActiveSessions,
304
+ getAllActiveSessions,
305
+ getActiveSession,
306
+ getSessionsForProject,
307
+ getSessionsForNavbar,
308
+ getSessionsByActivity,
309
+ getTotalRunningSessionCount,
310
+ hasBackgroundRunningSessions,
311
+ getAllProjectActivity,
312
+ useActiveSessionsState,
313
+ useActiveSessions,
314
+ useActiveSession,
315
+ useProjectSessions,
316
+ useNavbarSessions,
317
+ useSessionsByActivity,
318
+ useProjectActivity,
319
+ useTotalRunningSessions,
320
+ useHasBackgroundRunningSessions
321
+ };
@@ -0,0 +1,61 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-RQHJBTEU.js";
4
+
5
+ // src/primitives/tabs.tsx
6
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
7
+ import * as React from "react";
8
+ import { jsx } from "react/jsx-runtime";
9
+ var Tabs = TabsPrimitive.Root;
10
+ var TabsList = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
11
+ const variants = {
12
+ default: "inline-flex h-10 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
13
+ pills: "inline-flex items-center gap-2",
14
+ underline: "inline-flex items-center gap-4 border-b border-border"
15
+ };
16
+ return /* @__PURE__ */ jsx(
17
+ TabsPrimitive.List,
18
+ {
19
+ ref,
20
+ className: cn(variants[variant], className),
21
+ ...props
22
+ }
23
+ );
24
+ });
25
+ TabsList.displayName = TabsPrimitive.List.displayName;
26
+ var TabsTrigger = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
27
+ const variants = {
28
+ default: "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
29
+ pills: "inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground data-[state=inactive]:text-muted-foreground data-[state=inactive]:hover:bg-muted",
30
+ underline: "inline-flex items-center justify-center whitespace-nowrap pb-3 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:text-foreground data-[state=inactive]:text-muted-foreground"
31
+ };
32
+ return /* @__PURE__ */ jsx(
33
+ TabsPrimitive.Trigger,
34
+ {
35
+ ref,
36
+ className: cn(variants[variant], className),
37
+ ...props
38
+ }
39
+ );
40
+ });
41
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
42
+ var TabsContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
43
+ TabsPrimitive.Content,
44
+ {
45
+ ref,
46
+ className: cn(
47
+ "mt-4 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
48
+ "data-[state=active]:fade-in-0 data-[state=active]:slide-in-from-bottom-2 data-[state=active]:animate-in",
49
+ className
50
+ ),
51
+ ...props
52
+ }
53
+ ));
54
+ TabsContent.displayName = TabsPrimitive.Content.displayName;
55
+
56
+ export {
57
+ Tabs,
58
+ TabsList,
59
+ TabsTrigger,
60
+ TabsContent
61
+ };
File without changes