@atercates/claude-deck 0.2.1

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 (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +123 -0
  3. package/app/api/claude/hidden/route.ts +66 -0
  4. package/app/api/claude/projects/[name]/sessions/route.ts +71 -0
  5. package/app/api/claude/projects/route.ts +44 -0
  6. package/app/api/code-search/available/route.ts +12 -0
  7. package/app/api/code-search/route.ts +47 -0
  8. package/app/api/dev-servers/[id]/logs/route.ts +23 -0
  9. package/app/api/dev-servers/[id]/restart/route.ts +20 -0
  10. package/app/api/dev-servers/[id]/route.ts +51 -0
  11. package/app/api/dev-servers/[id]/stop/route.ts +20 -0
  12. package/app/api/dev-servers/detect/route.ts +39 -0
  13. package/app/api/dev-servers/route.ts +48 -0
  14. package/app/api/exec/route.ts +60 -0
  15. package/app/api/files/content/route.ts +76 -0
  16. package/app/api/files/route.ts +37 -0
  17. package/app/api/files/upload-temp/route.ts +41 -0
  18. package/app/api/git/check/route.ts +54 -0
  19. package/app/api/git/clone/route.ts +99 -0
  20. package/app/api/git/commit/route.ts +75 -0
  21. package/app/api/git/discard/route.ts +38 -0
  22. package/app/api/git/file-content/route.ts +64 -0
  23. package/app/api/git/history/[hash]/diff/route.ts +38 -0
  24. package/app/api/git/history/[hash]/route.ts +34 -0
  25. package/app/api/git/history/route.ts +27 -0
  26. package/app/api/git/multi-status/route.ts +46 -0
  27. package/app/api/git/pr/route.ts +164 -0
  28. package/app/api/git/push/route.ts +64 -0
  29. package/app/api/git/stage/route.ts +40 -0
  30. package/app/api/git/status/route.ts +51 -0
  31. package/app/api/git/unstage/route.ts +46 -0
  32. package/app/api/groups/[...path]/route.ts +136 -0
  33. package/app/api/groups/route.ts +93 -0
  34. package/app/api/orchestrate/spawn/route.ts +45 -0
  35. package/app/api/orchestrate/workers/[id]/route.ts +89 -0
  36. package/app/api/orchestrate/workers/route.ts +31 -0
  37. package/app/api/projects/[id]/detect/route.ts +27 -0
  38. package/app/api/projects/[id]/dev-servers/[dsId]/route.ts +66 -0
  39. package/app/api/projects/[id]/dev-servers/route.ts +51 -0
  40. package/app/api/projects/[id]/repositories/[repoId]/route.ts +67 -0
  41. package/app/api/projects/[id]/repositories/route.ts +74 -0
  42. package/app/api/projects/[id]/route.ts +108 -0
  43. package/app/api/projects/detect/route.ts +33 -0
  44. package/app/api/projects/route.ts +59 -0
  45. package/app/api/sessions/[id]/claude-session/route.ts +42 -0
  46. package/app/api/sessions/[id]/fork/route.ts +74 -0
  47. package/app/api/sessions/[id]/mcp-config/route.ts +34 -0
  48. package/app/api/sessions/[id]/messages/route.ts +60 -0
  49. package/app/api/sessions/[id]/pr/route.ts +188 -0
  50. package/app/api/sessions/[id]/preview/route.ts +42 -0
  51. package/app/api/sessions/[id]/route.ts +229 -0
  52. package/app/api/sessions/[id]/send-keys/route.ts +119 -0
  53. package/app/api/sessions/[id]/summarize/route.ts +331 -0
  54. package/app/api/sessions/init-script/route.ts +84 -0
  55. package/app/api/sessions/route.ts +209 -0
  56. package/app/api/sessions/status/route.ts +237 -0
  57. package/app/api/system/route.ts +9 -0
  58. package/app/api/tmux/kill-all/route.ts +57 -0
  59. package/app/api/tmux/rename/route.ts +30 -0
  60. package/app/globals.css +174 -0
  61. package/app/icon.svg +11 -0
  62. package/app/layout.tsx +122 -0
  63. package/app/page.tsx +629 -0
  64. package/components/ChatMessage.tsx +65 -0
  65. package/components/ChatView.tsx +276 -0
  66. package/components/ClaudeProjects/ClaudeProjectCard.tsx +195 -0
  67. package/components/ClaudeProjects/ClaudeProjectsSection.tsx +89 -0
  68. package/components/ClaudeProjects/ClaudeSessionCard.tsx +100 -0
  69. package/components/ClaudeProjects/index.ts +1 -0
  70. package/components/CodeSearch/CodeSearchResults.tsx +177 -0
  71. package/components/ConductorPanel.tsx +256 -0
  72. package/components/DevServers/DevServerCard.tsx +311 -0
  73. package/components/DevServers/DevServersSection.tsx +91 -0
  74. package/components/DevServers/ServerLogsModal.tsx +151 -0
  75. package/components/DevServers/StartServerDialog.tsx +359 -0
  76. package/components/DevServers/index.ts +4 -0
  77. package/components/DiffViewer/DiffModal.tsx +151 -0
  78. package/components/DiffViewer/UnifiedDiff.tsx +185 -0
  79. package/components/DiffViewer/index.tsx +2 -0
  80. package/components/DirectoryPicker.tsx +355 -0
  81. package/components/FileExplorer/FileEditor.tsx +276 -0
  82. package/components/FileExplorer/FileTabs.tsx +118 -0
  83. package/components/FileExplorer/FileTree.tsx +214 -0
  84. package/components/FileExplorer/HtmlRenderer.tsx +16 -0
  85. package/components/FileExplorer/MarkdownRenderer.tsx +18 -0
  86. package/components/FileExplorer/index.tsx +520 -0
  87. package/components/FilePicker.tsx +339 -0
  88. package/components/FolderPicker.tsx +201 -0
  89. package/components/GitDrawer/FileEditDialog.tsx +400 -0
  90. package/components/GitDrawer/index.tsx +464 -0
  91. package/components/GitPanel/CommitForm.tsx +205 -0
  92. package/components/GitPanel/CommitHistory.tsx +174 -0
  93. package/components/GitPanel/CommitItem.tsx +196 -0
  94. package/components/GitPanel/FileChanges.tsx +414 -0
  95. package/components/GitPanel/GitPanelTabs.tsx +39 -0
  96. package/components/GitPanel/index.tsx +817 -0
  97. package/components/MessageInput.tsx +82 -0
  98. package/components/NewClaudeSessionDialog.tsx +166 -0
  99. package/components/NewSessionDialog/AdvancedSettings.tsx +78 -0
  100. package/components/NewSessionDialog/AgentSelector.tsx +37 -0
  101. package/components/NewSessionDialog/CreatingOverlay.tsx +94 -0
  102. package/components/NewSessionDialog/NewSessionDialog.types.ts +136 -0
  103. package/components/NewSessionDialog/ProjectSelector.tsx +146 -0
  104. package/components/NewSessionDialog/WorkingDirectoryInput.tsx +55 -0
  105. package/components/NewSessionDialog/WorktreeSection.tsx +92 -0
  106. package/components/NewSessionDialog/hooks/useNewSessionForm.ts +370 -0
  107. package/components/NewSessionDialog/index.tsx +106 -0
  108. package/components/NotificationSettings.tsx +127 -0
  109. package/components/PRCreationModal.tsx +272 -0
  110. package/components/Pane/DesktopTabBar.tsx +353 -0
  111. package/components/Pane/MobileTabBar.tsx +210 -0
  112. package/components/Pane/OpenInVSCode.tsx +69 -0
  113. package/components/Pane/PaneSkeletons.tsx +57 -0
  114. package/components/Pane/index.tsx +558 -0
  115. package/components/PaneLayout.tsx +60 -0
  116. package/components/Projects/DevServersSection.tsx +140 -0
  117. package/components/Projects/DirectoryField.tsx +92 -0
  118. package/components/Projects/NewProjectDialog.tsx +188 -0
  119. package/components/Projects/NewProjectDialog.types.ts +46 -0
  120. package/components/Projects/ProjectCard.tsx +276 -0
  121. package/components/Projects/ProjectSettingsDialog.tsx +811 -0
  122. package/components/Projects/hooks/useNewProjectForm.ts +249 -0
  123. package/components/Projects/index.ts +3 -0
  124. package/components/Providers.tsx +49 -0
  125. package/components/QuickSwitcher.tsx +306 -0
  126. package/components/SessionList/KillAllConfirm.tsx +46 -0
  127. package/components/SessionList/SelectionToolbar.tsx +164 -0
  128. package/components/SessionList/SessionList.types.ts +37 -0
  129. package/components/SessionList/SessionListHeader.tsx +71 -0
  130. package/components/SessionList/hooks/useSessionListMutations.ts +269 -0
  131. package/components/SessionList/index.tsx +189 -0
  132. package/components/ShellDrawer/index.tsx +106 -0
  133. package/components/SidebarFooter.tsx +55 -0
  134. package/components/Terminal/KeybarToggleButton.tsx +45 -0
  135. package/components/Terminal/ScrollToBottomButton.tsx +32 -0
  136. package/components/Terminal/SearchBar.tsx +71 -0
  137. package/components/Terminal/TerminalToolbar.tsx +551 -0
  138. package/components/Terminal/VirtualKeyboard.tsx +711 -0
  139. package/components/Terminal/constants.ts +20 -0
  140. package/components/Terminal/hooks/index.ts +5 -0
  141. package/components/Terminal/hooks/resize-handlers.ts +140 -0
  142. package/components/Terminal/hooks/terminal-init.ts +151 -0
  143. package/components/Terminal/hooks/touch-scroll.ts +155 -0
  144. package/components/Terminal/hooks/useTerminalConnection.ts +282 -0
  145. package/components/Terminal/hooks/useTerminalConnection.types.ts +39 -0
  146. package/components/Terminal/hooks/useTerminalSearch.ts +103 -0
  147. package/components/Terminal/hooks/websocket-connection.ts +274 -0
  148. package/components/Terminal/index.tsx +320 -0
  149. package/components/ThemeToggle.tsx +168 -0
  150. package/components/TmuxSessions.tsx +132 -0
  151. package/components/ToolCallDisplay.tsx +71 -0
  152. package/components/WorkerCard.tsx +245 -0
  153. package/components/a/ABadge.tsx +115 -0
  154. package/components/a/AButton.tsx +163 -0
  155. package/components/a/ADialog.tsx +93 -0
  156. package/components/a/ADropdownMenu.tsx +279 -0
  157. package/components/a/AIconButton.tsx +190 -0
  158. package/components/a/ASheet.tsx +150 -0
  159. package/components/a/ATooltip.tsx +77 -0
  160. package/components/a/index.ts +64 -0
  161. package/components/mobile/SwipeSidebar.tsx +122 -0
  162. package/components/ui/badge.tsx +41 -0
  163. package/components/ui/button.tsx +60 -0
  164. package/components/ui/context-menu.tsx +197 -0
  165. package/components/ui/dialog.tsx +143 -0
  166. package/components/ui/dropdown-menu.tsx +257 -0
  167. package/components/ui/input.tsx +21 -0
  168. package/components/ui/scroll-area.tsx +52 -0
  169. package/components/ui/select.tsx +159 -0
  170. package/components/ui/skeleton.tsx +111 -0
  171. package/components/ui/switch.tsx +31 -0
  172. package/components/ui/textarea.tsx +21 -0
  173. package/components/ui/tooltip.tsx +32 -0
  174. package/components/views/DesktopView.tsx +244 -0
  175. package/components/views/MobileView.tsx +110 -0
  176. package/components/views/types.ts +75 -0
  177. package/contexts/PaneContext.tsx +336 -0
  178. package/data/claude/index.ts +9 -0
  179. package/data/claude/keys.ts +6 -0
  180. package/data/claude/queries.ts +120 -0
  181. package/data/claude/useClaudeUpdates.ts +37 -0
  182. package/data/code-search/index.ts +2 -0
  183. package/data/code-search/keys.ts +7 -0
  184. package/data/code-search/queries.ts +61 -0
  185. package/data/dev-servers/index.ts +8 -0
  186. package/data/dev-servers/keys.ts +4 -0
  187. package/data/dev-servers/queries.ts +104 -0
  188. package/data/files/index.ts +3 -0
  189. package/data/files/keys.ts +4 -0
  190. package/data/files/queries.ts +25 -0
  191. package/data/git/keys.ts +15 -0
  192. package/data/git/queries.ts +395 -0
  193. package/data/groups/index.ts +1 -0
  194. package/data/groups/mutations.ts +95 -0
  195. package/data/projects/index.ts +10 -0
  196. package/data/projects/keys.ts +4 -0
  197. package/data/projects/queries.ts +193 -0
  198. package/data/repositories/index.ts +7 -0
  199. package/data/repositories/keys.ts +5 -0
  200. package/data/repositories/queries.ts +122 -0
  201. package/data/sessions/index.ts +12 -0
  202. package/data/sessions/keys.ts +8 -0
  203. package/data/sessions/queries.ts +218 -0
  204. package/data/statuses/index.ts +1 -0
  205. package/data/statuses/queries.ts +69 -0
  206. package/hooks/useCopyToClipboard.ts +48 -0
  207. package/hooks/useDevServersManager.ts +73 -0
  208. package/hooks/useDirectoryBrowser.ts +90 -0
  209. package/hooks/useDrawerAnimation.ts +27 -0
  210. package/hooks/useFileDrop.ts +87 -0
  211. package/hooks/useFileEditor.ts +184 -0
  212. package/hooks/useGroups.ts +37 -0
  213. package/hooks/useHomePath.ts +34 -0
  214. package/hooks/useKeyRepeat.ts +55 -0
  215. package/hooks/useKeybarVisibility.ts +42 -0
  216. package/hooks/useNotifications.ts +257 -0
  217. package/hooks/useProjects.ts +53 -0
  218. package/hooks/useSessionStatuses.ts +30 -0
  219. package/hooks/useSessions.ts +86 -0
  220. package/hooks/useSpeechRecognition.ts +124 -0
  221. package/hooks/useViewport.ts +32 -0
  222. package/hooks/useViewportHeight.ts +50 -0
  223. package/lib/async-operations.ts +35 -0
  224. package/lib/banner.ts +81 -0
  225. package/lib/claude/jsonl-cache.ts +86 -0
  226. package/lib/claude/jsonl-reader.ts +271 -0
  227. package/lib/claude/process-manager.ts +278 -0
  228. package/lib/claude/stream-parser.ts +173 -0
  229. package/lib/claude/types.ts +154 -0
  230. package/lib/claude/watcher.ts +71 -0
  231. package/lib/client/session-registry.ts +111 -0
  232. package/lib/code-search.ts +121 -0
  233. package/lib/db/index.ts +48 -0
  234. package/lib/db/migrations.ts +45 -0
  235. package/lib/db/queries.ts +460 -0
  236. package/lib/db/schema.ts +114 -0
  237. package/lib/db/types.ts +92 -0
  238. package/lib/db.ts +2 -0
  239. package/lib/dev-servers.ts +509 -0
  240. package/lib/diff-parser.ts +221 -0
  241. package/lib/env-setup.ts +285 -0
  242. package/lib/file-upload.ts +34 -0
  243. package/lib/file-utils.ts +50 -0
  244. package/lib/files.ts +207 -0
  245. package/lib/git-history.ts +294 -0
  246. package/lib/git-status.ts +391 -0
  247. package/lib/git.ts +257 -0
  248. package/lib/mcp-config.ts +81 -0
  249. package/lib/multi-repo-git.ts +179 -0
  250. package/lib/notifications.ts +219 -0
  251. package/lib/orchestration.ts +448 -0
  252. package/lib/panes.ts +232 -0
  253. package/lib/ports.ts +97 -0
  254. package/lib/pr-generation.ts +307 -0
  255. package/lib/pr.ts +234 -0
  256. package/lib/projects.ts +578 -0
  257. package/lib/providers/registry.ts +70 -0
  258. package/lib/providers.ts +121 -0
  259. package/lib/query-client.ts +14 -0
  260. package/lib/rangeSelectionUtils.ts +65 -0
  261. package/lib/status-detector.ts +375 -0
  262. package/lib/terminal-themes.ts +265 -0
  263. package/lib/theme-config.ts +327 -0
  264. package/lib/utils.ts +6 -0
  265. package/lib/worktrees.ts +262 -0
  266. package/mcp/orchestration-server.ts +438 -0
  267. package/package.json +139 -0
  268. package/postcss.config.mjs +7 -0
  269. package/public/icon.svg +10 -0
  270. package/public/icons/icon-128x128.png +0 -0
  271. package/public/icons/icon-144x144.png +0 -0
  272. package/public/icons/icon-152x152.png +0 -0
  273. package/public/icons/icon-192x192.png +0 -0
  274. package/public/icons/icon-384x384.png +0 -0
  275. package/public/icons/icon-512x512.png +0 -0
  276. package/public/icons/icon-72x72.png +0 -0
  277. package/public/icons/icon-96x96.png +0 -0
  278. package/public/manifest.json +61 -0
  279. package/public/sw.js +64 -0
  280. package/scripts/agent-os +91 -0
  281. package/scripts/install.sh +48 -0
  282. package/scripts/lib/ai-clis.sh +132 -0
  283. package/scripts/lib/commands.sh +487 -0
  284. package/scripts/lib/common.sh +89 -0
  285. package/scripts/lib/prerequisites.sh +462 -0
  286. package/scripts/setup.sh +134 -0
  287. package/server.ts +155 -0
  288. package/stores/fileOpen.ts +26 -0
  289. package/stores/index.ts +1 -0
  290. package/stores/initialPrompt.ts +24 -0
  291. package/stores/sessionSelection.ts +48 -0
  292. package/styles/themes.css +603 -0
  293. package/tsconfig.json +33 -0
@@ -0,0 +1,245 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Button } from "./ui/button";
5
+ import { cn } from "@/lib/utils";
6
+ import {
7
+ Circle,
8
+ Loader2,
9
+ AlertCircle,
10
+ CheckCircle,
11
+ XCircle,
12
+ Eye,
13
+ Send,
14
+ X,
15
+ ChevronDown,
16
+ ChevronRight,
17
+ GitBranch,
18
+ } from "lucide-react";
19
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
20
+
21
+ export type WorkerStatus =
22
+ | "pending"
23
+ | "running"
24
+ | "waiting"
25
+ | "idle"
26
+ | "completed"
27
+ | "failed"
28
+ | "dead";
29
+
30
+ export interface WorkerInfo {
31
+ id: string;
32
+ name: string;
33
+ task: string;
34
+ status: WorkerStatus;
35
+ worktreePath: string | null;
36
+ branchName: string | null;
37
+ createdAt: string;
38
+ }
39
+
40
+ interface WorkerCardProps {
41
+ worker: WorkerInfo;
42
+ isExpanded?: boolean;
43
+ output?: string;
44
+ onToggleExpand?: () => void;
45
+ onViewOutput?: () => void;
46
+ onSendMessage?: (message: string) => void;
47
+ onKill?: () => void;
48
+ onAttach?: () => void;
49
+ }
50
+
51
+ const statusConfig: Record<
52
+ WorkerStatus,
53
+ { icon: typeof Circle; color: string; label: string }
54
+ > = {
55
+ pending: { icon: Circle, color: "text-muted-foreground", label: "Pending" },
56
+ running: { icon: Loader2, color: "text-green-500", label: "Running" },
57
+ waiting: { icon: AlertCircle, color: "text-yellow-500", label: "Waiting" },
58
+ idle: { icon: Circle, color: "text-muted-foreground", label: "Idle" },
59
+ completed: { icon: CheckCircle, color: "text-green-500", label: "Completed" },
60
+ failed: { icon: XCircle, color: "text-red-500", label: "Failed" },
61
+ dead: { icon: XCircle, color: "text-red-500", label: "Dead" },
62
+ };
63
+
64
+ export function WorkerCard({
65
+ worker,
66
+ isExpanded = false,
67
+ output,
68
+ onToggleExpand,
69
+ onViewOutput,
70
+ onSendMessage,
71
+ onKill,
72
+ onAttach,
73
+ }: WorkerCardProps) {
74
+ const [message, setMessage] = useState("");
75
+ const [showSendInput, setShowSendInput] = useState(false);
76
+
77
+ const config = statusConfig[worker.status];
78
+ const StatusIcon = config.icon;
79
+ const isActive = worker.status === "running" || worker.status === "waiting";
80
+
81
+ const handleSend = () => {
82
+ if (message.trim() && onSendMessage) {
83
+ onSendMessage(message.trim());
84
+ setMessage("");
85
+ setShowSendInput(false);
86
+ }
87
+ };
88
+
89
+ return (
90
+ <div
91
+ className={cn(
92
+ "bg-card rounded-lg border transition-colors",
93
+ isActive && "border-primary/30",
94
+ worker.status === "completed" && "border-green-500/30 bg-green-500/5",
95
+ worker.status === "failed" && "border-red-500/30 bg-red-500/5"
96
+ )}
97
+ >
98
+ {/* Header */}
99
+ <div
100
+ className="hover:bg-accent/30 flex cursor-pointer items-center gap-2 p-3"
101
+ onClick={onToggleExpand}
102
+ >
103
+ <button className="p-0.5">
104
+ {isExpanded ? (
105
+ <ChevronDown className="text-muted-foreground h-3 w-3" />
106
+ ) : (
107
+ <ChevronRight className="text-muted-foreground h-3 w-3" />
108
+ )}
109
+ </button>
110
+
111
+ <StatusIcon
112
+ className={cn(
113
+ "h-4 w-4 flex-shrink-0",
114
+ config.color,
115
+ worker.status === "running" && "animate-spin"
116
+ )}
117
+ />
118
+
119
+ <div className="min-w-0 flex-1">
120
+ <div className="flex items-center gap-2">
121
+ <span className="truncate text-sm font-medium">{worker.name}</span>
122
+ <span
123
+ className={cn(
124
+ "rounded px-1.5 py-0.5 text-xs",
125
+ config.color,
126
+ "bg-current/10"
127
+ )}
128
+ >
129
+ {config.label}
130
+ </span>
131
+ </div>
132
+ {worker.branchName && (
133
+ <div className="text-muted-foreground mt-0.5 flex items-center gap-1 text-xs">
134
+ <GitBranch className="h-3 w-3" />
135
+ <span className="truncate">{worker.branchName}</span>
136
+ </div>
137
+ )}
138
+ </div>
139
+
140
+ {/* Quick actions */}
141
+ <div className="flex gap-1" onClick={(e) => e.stopPropagation()}>
142
+ {isActive && onAttach && (
143
+ <Tooltip>
144
+ <TooltipTrigger asChild>
145
+ <Button variant="ghost" size="icon-sm" onClick={onAttach}>
146
+ <Eye className="h-3 w-3" />
147
+ </Button>
148
+ </TooltipTrigger>
149
+ <TooltipContent>Attach to terminal</TooltipContent>
150
+ </Tooltip>
151
+ )}
152
+ {isActive && onKill && (
153
+ <Tooltip>
154
+ <TooltipTrigger asChild>
155
+ <Button
156
+ variant="ghost"
157
+ size="icon-sm"
158
+ onClick={onKill}
159
+ className="text-red-500 hover:text-red-400"
160
+ >
161
+ <X className="h-3 w-3" />
162
+ </Button>
163
+ </TooltipTrigger>
164
+ <TooltipContent>Kill worker</TooltipContent>
165
+ </Tooltip>
166
+ )}
167
+ </div>
168
+ </div>
169
+
170
+ {/* Expanded content */}
171
+ {isExpanded && (
172
+ <div className="border-border/50 space-y-2 border-t px-3 pb-3">
173
+ {/* Task */}
174
+ <div className="pt-2">
175
+ <div className="text-muted-foreground mb-1 text-xs">Task:</div>
176
+ <div className="bg-muted/30 rounded p-2 font-mono text-sm text-xs">
177
+ {worker.task}
178
+ </div>
179
+ </div>
180
+
181
+ {/* Output preview */}
182
+ {output && (
183
+ <div>
184
+ <div className="text-muted-foreground mb-1 text-xs">
185
+ Recent output:
186
+ </div>
187
+ <pre className="bg-muted/30 max-h-32 overflow-x-auto overflow-y-auto rounded p-2 font-mono text-xs whitespace-pre-wrap">
188
+ {output.slice(-500)}
189
+ </pre>
190
+ </div>
191
+ )}
192
+
193
+ {/* Actions */}
194
+ <div className="flex gap-2 pt-1">
195
+ {onViewOutput && (
196
+ <Button variant="outline" size="sm" onClick={onViewOutput}>
197
+ <Eye className="mr-1 h-3 w-3" />
198
+ Full output
199
+ </Button>
200
+ )}
201
+
202
+ {isActive && onSendMessage && !showSendInput && (
203
+ <Button
204
+ variant="outline"
205
+ size="sm"
206
+ onClick={() => setShowSendInput(true)}
207
+ >
208
+ <Send className="mr-1 h-3 w-3" />
209
+ Send input
210
+ </Button>
211
+ )}
212
+ </div>
213
+
214
+ {/* Send input form */}
215
+ {showSendInput && (
216
+ <div className="flex gap-2">
217
+ <input
218
+ type="text"
219
+ value={message}
220
+ onChange={(e) => setMessage(e.target.value)}
221
+ onKeyDown={(e) => {
222
+ if (e.key === "Enter") handleSend();
223
+ if (e.key === "Escape") setShowSendInput(false);
224
+ }}
225
+ placeholder="Type message..."
226
+ className="bg-muted/50 focus:bg-muted focus:ring-primary/50 flex-1 rounded px-2 py-1 text-sm focus:ring-1 focus:outline-none"
227
+ autoFocus
228
+ />
229
+ <Button size="sm" onClick={handleSend}>
230
+ Send
231
+ </Button>
232
+ <Button
233
+ size="sm"
234
+ variant="ghost"
235
+ onClick={() => setShowSendInput(false)}
236
+ >
237
+ Cancel
238
+ </Button>
239
+ </div>
240
+ )}
241
+ </div>
242
+ )}
243
+ </div>
244
+ );
245
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * ABadge - Standard badge component for the design system
3
+ *
4
+ * A consistent badge for labels, statuses, and feature indicators.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * // Simple badge
9
+ * <ABadge>Default</ABadge>
10
+ *
11
+ * // New feature indicator
12
+ * <ABadge variant="new">New</ABadge>
13
+ *
14
+ * // Beta indicator
15
+ * <ABadge variant="beta">Beta</ABadge>
16
+ *
17
+ * // Small size
18
+ * <ABadge size="sm" variant="new">New</ABadge>
19
+ * ```
20
+ */
21
+
22
+ "use client";
23
+
24
+ import { Badge } from "@/components/ui/badge";
25
+ import { cn } from "@/lib/utils";
26
+
27
+ export type ABadgeSize = "sm" | "md";
28
+ export type ABadgeVariant =
29
+ | "default"
30
+ | "secondary"
31
+ | "destructive"
32
+ | "outline"
33
+ | "new"
34
+ | "beta"
35
+ | "pro"
36
+ | "waiting"
37
+ | "running"
38
+ | "idle";
39
+
40
+ export interface ABadgeProps {
41
+ /** Badge content */
42
+ children: React.ReactNode;
43
+ /** Badge style variant */
44
+ variant?: ABadgeVariant;
45
+ /** Badge size */
46
+ size?: ABadgeSize;
47
+ /** Additional className */
48
+ className?: string;
49
+ }
50
+
51
+ const SIZE_CLASSES: Record<ABadgeSize, string> = {
52
+ sm: "h-4 px-1.5 py-0 text-[10px] leading-none",
53
+ md: "h-5 px-2 py-0 text-xs leading-none",
54
+ };
55
+
56
+ const VARIANT_CLASSES: Record<ABadgeVariant, string> = {
57
+ default: "",
58
+ secondary: "",
59
+ destructive: "",
60
+ outline: "",
61
+ new: "border-transparent bg-emerald-500/15 text-emerald-600 dark:bg-emerald-500/20 dark:text-emerald-400",
62
+ beta: "border-transparent bg-amber-500/15 text-amber-600 dark:bg-amber-500/20 dark:text-amber-400",
63
+ pro: "border-transparent bg-violet-500/15 text-violet-600 dark:bg-violet-500/20 dark:text-violet-400",
64
+ waiting:
65
+ "border-transparent bg-yellow-500/15 text-yellow-600 dark:bg-yellow-500/20 dark:text-yellow-400",
66
+ running:
67
+ "border-transparent bg-green-500/15 text-green-600 dark:bg-green-500/20 dark:text-green-400",
68
+ idle: "border-transparent bg-gray-500/15 text-gray-600 dark:bg-gray-500/20 dark:text-gray-400",
69
+ };
70
+
71
+ const BASE_VARIANT_MAP: Record<
72
+ ABadgeVariant,
73
+ "default" | "secondary" | "destructive" | "outline"
74
+ > = {
75
+ default: "default",
76
+ secondary: "secondary",
77
+ destructive: "destructive",
78
+ outline: "outline",
79
+ new: "secondary",
80
+ beta: "secondary",
81
+ pro: "secondary",
82
+ waiting: "secondary",
83
+ running: "secondary",
84
+ idle: "secondary",
85
+ };
86
+
87
+ export function ABadge({
88
+ children,
89
+ variant = "default",
90
+ size = "md",
91
+ className,
92
+ }: ABadgeProps) {
93
+ const isCustomVariant = [
94
+ "new",
95
+ "beta",
96
+ "pro",
97
+ "waiting",
98
+ "running",
99
+ "idle",
100
+ ].includes(variant);
101
+
102
+ return (
103
+ <Badge
104
+ variant={BASE_VARIANT_MAP[variant]}
105
+ className={cn(
106
+ "flex items-center justify-center font-medium",
107
+ SIZE_CLASSES[size],
108
+ isCustomVariant && VARIANT_CLASSES[variant],
109
+ className
110
+ )}
111
+ >
112
+ {children}
113
+ </Badge>
114
+ );
115
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * AButton - Standard button component for the design system
3
+ *
4
+ * A consistent button with optional tooltip, loading state, and icon support.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * // Simple button
9
+ * <AButton onClick={handleClick}>Save</AButton>
10
+ *
11
+ * // With icon
12
+ * <AButton icon={Plus} onClick={handleClick}>Add item</AButton>
13
+ *
14
+ * // With tooltip
15
+ * <AButton tooltip="Save changes" onClick={handleClick}>Save</AButton>
16
+ *
17
+ * // Loading state
18
+ * <AButton loading onClick={handleClick}>Saving...</AButton>
19
+ *
20
+ * // Ghost variant (no background/border)
21
+ * <AButton variant="ghost" onClick={handleClick}>Cancel</AButton>
22
+ * ```
23
+ */
24
+
25
+ "use client";
26
+
27
+ import { forwardRef } from "react";
28
+ import type { LucideIcon } from "lucide-react";
29
+ import { Loader2 } from "lucide-react";
30
+ import { Button } from "@/components/ui/button";
31
+ import { ATooltip } from "@/components/a/ATooltip";
32
+ import { cn } from "@/lib/utils";
33
+
34
+ export type AButtonSize = "sm" | "md" | "lg";
35
+ export type AButtonVariant =
36
+ | "default"
37
+ | "ghost"
38
+ | "outline"
39
+ | "secondary"
40
+ | "destructive"
41
+ | "link";
42
+
43
+ export interface AButtonProps extends Omit<
44
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
45
+ "children"
46
+ > {
47
+ /** Button content */
48
+ children?: React.ReactNode;
49
+ /** Optional Lucide icon component (shown before text) */
50
+ icon?: LucideIcon;
51
+ /** Optional icon shown after text */
52
+ iconRight?: LucideIcon;
53
+ /** Optional tooltip text */
54
+ tooltip?: string;
55
+ /** Optional keyboard shortcut hint for tooltip */
56
+ shortcut?: string;
57
+ /** Tooltip position */
58
+ tooltipSide?: "top" | "right" | "bottom" | "left";
59
+ /** Loading state - shows spinner and disables button */
60
+ loading?: boolean;
61
+ /** Button size variant */
62
+ size?: AButtonSize;
63
+ /** Button style variant */
64
+ variant?: AButtonVariant;
65
+ /** Make the button take full width */
66
+ fullWidth?: boolean;
67
+ /** Additional className for the icon */
68
+ iconClassName?: string;
69
+ /** Additional className */
70
+ className?: string;
71
+ }
72
+
73
+ const SIZE_CLASSES: Record<AButtonSize, { button: string; icon: string }> = {
74
+ sm: { button: "h-7 px-2 text-xs", icon: "h-3.5 w-3.5" },
75
+ md: { button: "h-8 px-3 text-sm", icon: "h-4 w-4" },
76
+ lg: { button: "h-10 px-4 text-base", icon: "h-5 w-5" },
77
+ };
78
+
79
+ const VARIANT_MAP: Record<
80
+ AButtonVariant,
81
+ "default" | "ghost" | "outline" | "secondary" | "destructive" | "link"
82
+ > = {
83
+ default: "default",
84
+ ghost: "ghost",
85
+ outline: "outline",
86
+ secondary: "secondary",
87
+ destructive: "destructive",
88
+ link: "link",
89
+ };
90
+
91
+ export const AButton = forwardRef<HTMLButtonElement, AButtonProps>(
92
+ (
93
+ {
94
+ children,
95
+ icon: Icon,
96
+ iconRight: IconRight,
97
+ tooltip,
98
+ shortcut,
99
+ tooltipSide = "bottom",
100
+ loading = false,
101
+ size = "md",
102
+ variant = "default",
103
+ fullWidth = false,
104
+ iconClassName,
105
+ className,
106
+ disabled,
107
+ ...props
108
+ },
109
+ ref
110
+ ) => {
111
+ const sizeClasses = SIZE_CLASSES[size];
112
+ const isDisabled = disabled || loading;
113
+
114
+ const button = (
115
+ <Button
116
+ ref={ref}
117
+ variant={VARIANT_MAP[variant]}
118
+ disabled={isDisabled}
119
+ className={cn(
120
+ sizeClasses.button,
121
+ fullWidth && "w-full",
122
+ variant === "ghost" &&
123
+ "border-0 shadow-none ring-0 focus:ring-0 focus-visible:ring-0",
124
+ className
125
+ )}
126
+ {...props}
127
+ >
128
+ {loading ? (
129
+ <Loader2 className={cn(sizeClasses.icon, "mr-2 animate-spin")} />
130
+ ) : Icon ? (
131
+ <Icon
132
+ className={cn(sizeClasses.icon, children && "mr-2", iconClassName)}
133
+ />
134
+ ) : null}
135
+
136
+ {children}
137
+
138
+ {IconRight && !loading && (
139
+ <IconRight
140
+ className={cn(sizeClasses.icon, children && "ml-2", iconClassName)}
141
+ />
142
+ )}
143
+ </Button>
144
+ );
145
+
146
+ if (tooltip) {
147
+ return (
148
+ <ATooltip
149
+ content={tooltip}
150
+ shortcut={shortcut}
151
+ side={tooltipSide}
152
+ disabled={isDisabled}
153
+ >
154
+ {button}
155
+ </ATooltip>
156
+ );
157
+ }
158
+
159
+ return button;
160
+ }
161
+ );
162
+
163
+ AButton.displayName = "AButton";
@@ -0,0 +1,93 @@
1
+ /**
2
+ * ADialog - Reusable dialog component for the design system
3
+ *
4
+ * A modal dialog with optional icon, header, scrollable content, and footer.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * <ADialog
9
+ * open={isOpen}
10
+ * onOpenChange={setIsOpen}
11
+ * title="Settings"
12
+ * description="Manage your preferences"
13
+ * icon={<Settings />}
14
+ * footer={<Button>Save</Button>}
15
+ * >
16
+ * <SettingsContent />
17
+ * </ADialog>
18
+ * ```
19
+ */
20
+
21
+ "use client";
22
+
23
+ import { ReactNode } from "react";
24
+ import {
25
+ Dialog,
26
+ DialogContent,
27
+ DialogDescription,
28
+ DialogHeader,
29
+ DialogTitle,
30
+ } from "@/components/ui/dialog";
31
+ import { cn } from "@/lib/utils";
32
+
33
+ export interface ADialogProps {
34
+ /** Controlled open state */
35
+ open: boolean;
36
+ /** Callback when open state changes */
37
+ onOpenChange: (open: boolean) => void;
38
+ /** Dialog title */
39
+ title: string;
40
+ /** Optional description below the title */
41
+ description?: string;
42
+ /** Optional icon to show next to title */
43
+ icon?: ReactNode;
44
+ /** Main content */
45
+ children: ReactNode;
46
+ /** Optional footer content (typically action buttons) */
47
+ footer?: ReactNode;
48
+ /** Maximum width class (default: "sm:max-w-lg") */
49
+ maxWidth?: string;
50
+ /** Additional className for the dialog content */
51
+ className?: string;
52
+ /** Additional className for the content container */
53
+ contentClassName?: string;
54
+ }
55
+
56
+ export function ADialog({
57
+ open,
58
+ onOpenChange,
59
+ title,
60
+ description,
61
+ icon,
62
+ children,
63
+ footer,
64
+ maxWidth = "sm:max-w-lg",
65
+ className,
66
+ contentClassName,
67
+ }: ADialogProps) {
68
+ return (
69
+ <Dialog open={open} onOpenChange={onOpenChange}>
70
+ <DialogContent className={cn(maxWidth, className)}>
71
+ <DialogHeader>
72
+ <div className="flex items-center gap-2">
73
+ {icon && (
74
+ <span className="text-foreground [&>svg]:h-5 [&>svg]:w-5">
75
+ {icon}
76
+ </span>
77
+ )}
78
+ <DialogTitle>{title}</DialogTitle>
79
+ </div>
80
+ {description && <DialogDescription>{description}</DialogDescription>}
81
+ </DialogHeader>
82
+
83
+ <div className={cn("py-4", contentClassName)}>{children}</div>
84
+
85
+ {footer && (
86
+ <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
87
+ {footer}
88
+ </div>
89
+ )}
90
+ </DialogContent>
91
+ </Dialog>
92
+ );
93
+ }