@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,189 @@
1
+ "use client";
2
+
3
+ import { useState, useRef, useCallback } from "react";
4
+ import { ClaudeProjectsSection } from "@/components/ClaudeProjects";
5
+ import { NewProjectDialog } from "@/components/Projects";
6
+ import { FolderPicker } from "@/components/FolderPicker";
7
+ import { SelectionToolbar } from "./SelectionToolbar";
8
+ import { SessionListHeader } from "./SessionListHeader";
9
+ import { KillAllConfirm } from "./KillAllConfirm";
10
+ import { useSessionListMutations } from "./hooks/useSessionListMutations";
11
+ import { ScrollArea } from "@/components/ui/scroll-area";
12
+ import { Button } from "@/components/ui/button";
13
+ import { Loader2, AlertCircle } from "lucide-react";
14
+ import type { Session } from "@/lib/db";
15
+ import { useViewport } from "@/hooks/useViewport";
16
+
17
+ import { useSessionsQuery } from "@/data/sessions";
18
+ import { useCreateProject } from "@/data/projects";
19
+ import { useClaudeProjectsQuery } from "@/data/claude";
20
+
21
+ import type { SessionListProps } from "./SessionList.types";
22
+
23
+ export type { SessionListProps } from "./SessionList.types";
24
+
25
+ export function SessionList({
26
+ activeSessionId,
27
+ sessionStatuses,
28
+ onSelect,
29
+ onOpenInTab,
30
+ onNewSessionInProject,
31
+ onOpenTerminal,
32
+ onStartDevServer,
33
+ onCreateDevServer,
34
+ onResumeClaudeSession,
35
+ onNewSession,
36
+ }: SessionListProps) {
37
+ const { isMobile } = useViewport();
38
+
39
+ const {
40
+ data: sessionsData,
41
+ isPending: isSessionsPending,
42
+ isError: isSessionsError,
43
+ error: sessionsError,
44
+ } = useSessionsQuery();
45
+
46
+ const { isPending: isClaudePending, isError: isClaudeError } =
47
+ useClaudeProjectsQuery();
48
+
49
+ const isInitialLoading = isSessionsPending || isClaudePending;
50
+ const hasError = isSessionsError || isClaudeError;
51
+
52
+ const sessions = sessionsData?.sessions ?? [];
53
+ const allSessionIds = sessions.map((s: Session) => s.id);
54
+
55
+ const mutations = useSessionListMutations({ onSelectSession: onSelect });
56
+ const createProject = useCreateProject();
57
+
58
+ const [showKillAllConfirm, setShowKillAllConfirm] = useState(false);
59
+ const [showNewProjectDialog, setShowNewProjectDialog] = useState(false);
60
+ const [newProjectMode, setNewProjectMode] = useState<"new" | "clone">("new");
61
+ const [showFolderPicker, setShowFolderPicker] = useState(false);
62
+
63
+ const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
64
+ const pendingHoverRef = useRef<{
65
+ session: Session;
66
+ rect: DOMRect;
67
+ } | null>(null);
68
+
69
+ const hoverHandlers = {
70
+ onHoverStart: useCallback(
71
+ (_session: Session, _rect: DOMRect) => {
72
+ if (isMobile) return;
73
+ },
74
+ [isMobile]
75
+ ),
76
+ onHoverEnd: useCallback(() => {
77
+ if (hoverTimeoutRef.current) {
78
+ clearTimeout(hoverTimeoutRef.current);
79
+ hoverTimeoutRef.current = null;
80
+ }
81
+ pendingHoverRef.current = null;
82
+ }, []),
83
+ };
84
+
85
+ return (
86
+ <div className="flex h-full flex-col overflow-hidden">
87
+ <SessionListHeader
88
+ onNewProject={() => {
89
+ setNewProjectMode("new");
90
+ setShowNewProjectDialog(true);
91
+ }}
92
+ onOpenProject={() => setShowFolderPicker(true)}
93
+ onCloneFromGithub={() => {
94
+ setNewProjectMode("clone");
95
+ setShowNewProjectDialog(true);
96
+ }}
97
+ onKillAll={() => setShowKillAllConfirm(true)}
98
+ />
99
+
100
+ {showKillAllConfirm && (
101
+ <KillAllConfirm
102
+ onCancel={() => setShowKillAllConfirm(false)}
103
+ onComplete={() => setShowKillAllConfirm(false)}
104
+ />
105
+ )}
106
+
107
+ <SelectionToolbar
108
+ allSessionIds={allSessionIds}
109
+ onDeleteSessions={mutations.handleBulkDelete}
110
+ />
111
+
112
+ {mutations.summarizingSessionId && (
113
+ <div className="bg-primary/10 mx-4 mb-2 flex items-center gap-2 rounded-lg p-2 text-sm">
114
+ <Loader2 className="text-primary h-4 w-4 animate-spin" />
115
+ <span className="text-primary">Generating summary...</span>
116
+ </div>
117
+ )}
118
+
119
+ <ScrollArea className="w-full flex-1">
120
+ <div className="max-w-full space-y-0.5 px-1.5 py-1">
121
+ {hasError && !isInitialLoading && (
122
+ <div className="flex flex-col items-center justify-center px-4 py-12">
123
+ <AlertCircle className="text-destructive/50 mb-3 h-10 w-10" />
124
+ <p className="text-destructive mb-2 text-sm">
125
+ Failed to load sessions
126
+ </p>
127
+ <p className="text-muted-foreground mb-4 text-xs">
128
+ {sessionsError?.message || "Unknown error"}
129
+ </p>
130
+ <Button
131
+ variant="outline"
132
+ onClick={mutations.handleRefresh}
133
+ className="gap-2"
134
+ >
135
+ Retry
136
+ </Button>
137
+ </div>
138
+ )}
139
+
140
+ {!isInitialLoading && !hasError && (
141
+ <ClaudeProjectsSection
142
+ onSelectSession={(claudeSessionId, cwd, summary, projectName) => {
143
+ onResumeClaudeSession?.(
144
+ claudeSessionId,
145
+ cwd,
146
+ summary,
147
+ projectName
148
+ );
149
+ }}
150
+ onNewSession={onNewSession}
151
+ />
152
+ )}
153
+ </div>
154
+ </ScrollArea>
155
+
156
+ <NewProjectDialog
157
+ open={showNewProjectDialog}
158
+ mode={newProjectMode}
159
+ onClose={() => setShowNewProjectDialog(false)}
160
+ onCreated={() => setShowNewProjectDialog(false)}
161
+ />
162
+
163
+ {showFolderPicker && (
164
+ <FolderPicker
165
+ initialPath="~"
166
+ onClose={() => setShowFolderPicker(false)}
167
+ onSelect={(path) => {
168
+ const parts = path.split("/").filter(Boolean);
169
+ const name = parts[parts.length - 1] || "project";
170
+
171
+ createProject.mutate(
172
+ {
173
+ name,
174
+ workingDirectory: path,
175
+ agentType: "claude",
176
+ defaultModel: "sonnet",
177
+ devServers: [],
178
+ },
179
+ {
180
+ onSuccess: () => setShowFolderPicker(false),
181
+ onError: () => setShowFolderPicker(false),
182
+ }
183
+ );
184
+ }}
185
+ />
186
+ )}
187
+ </div>
188
+ );
189
+ }
@@ -0,0 +1,106 @@
1
+ "use client";
2
+
3
+ import { useRef, useCallback, useEffect } from "react";
4
+ import dynamic from "next/dynamic";
5
+ import { X, Copy, Check } from "lucide-react";
6
+ import { Button } from "@/components/ui/button";
7
+ import { cn } from "@/lib/utils";
8
+ import { useDrawerAnimation } from "@/hooks/useDrawerAnimation";
9
+ import { useCopyToClipboard } from "@/hooks/useCopyToClipboard";
10
+ import type { TerminalHandle } from "@/components/Terminal";
11
+
12
+ const Terminal = dynamic(
13
+ () => import("@/components/Terminal").then((mod) => mod.Terminal),
14
+ { ssr: false }
15
+ );
16
+
17
+ interface ShellDrawerProps {
18
+ open: boolean;
19
+ onOpenChange: (open: boolean) => void;
20
+ workingDirectory: string;
21
+ }
22
+
23
+ export function ShellDrawer({
24
+ open,
25
+ onOpenChange,
26
+ workingDirectory,
27
+ }: ShellDrawerProps) {
28
+ const terminalRef = useRef<TerminalHandle | null>(null);
29
+ const hasInitialized = useRef(false);
30
+ const { copied, copy } = useCopyToClipboard();
31
+
32
+ // Animation
33
+ const isAnimatingIn = useDrawerAnimation(open);
34
+
35
+ // When terminal connects, cd to working directory
36
+ const handleConnected = useCallback(() => {
37
+ if (terminalRef.current && workingDirectory && !hasInitialized.current) {
38
+ hasInitialized.current = true;
39
+ // Clear any existing command, cd to directory, and clear screen
40
+ setTimeout(() => {
41
+ terminalRef.current?.sendInput("\x15"); // Ctrl+U to clear line
42
+ setTimeout(() => {
43
+ terminalRef.current?.sendCommand(`cd ${workingDirectory} && clear`);
44
+ }, 50);
45
+ }, 100);
46
+ }
47
+ }, [workingDirectory]);
48
+
49
+ // Reset initialization flag when drawer closes
50
+ useEffect(() => {
51
+ if (!open) {
52
+ hasInitialized.current = false;
53
+ }
54
+ }, [open]);
55
+
56
+ if (!open) return null;
57
+
58
+ return (
59
+ <div
60
+ className={cn(
61
+ "bg-muted/30 flex h-full flex-col transition-all duration-200 ease-out",
62
+ isAnimatingIn ? "translate-y-0 opacity-100" : "translate-y-4 opacity-0"
63
+ )}
64
+ >
65
+ {/* Header */}
66
+ <div className="flex items-center justify-between px-3 py-1.5">
67
+ <div className="flex items-center gap-2">
68
+ <span className="text-muted-foreground font-mono text-sm">
69
+ {">_"}
70
+ </span>
71
+ <span className="text-muted-foreground truncate text-xs">
72
+ {workingDirectory}
73
+ </span>
74
+ <button
75
+ onClick={() => copy(workingDirectory)}
76
+ className="text-muted-foreground hover:text-foreground flex-shrink-0 p-0.5"
77
+ title="Copy path"
78
+ >
79
+ {copied ? (
80
+ <Check className="h-3 w-3 text-green-500" />
81
+ ) : (
82
+ <Copy className="h-3 w-3" />
83
+ )}
84
+ </button>
85
+ </div>
86
+ <Button
87
+ variant="ghost"
88
+ size="icon"
89
+ onClick={() => onOpenChange(false)}
90
+ className="h-7 w-7"
91
+ >
92
+ <X className="h-3.5 w-3.5" />
93
+ </Button>
94
+ </div>
95
+
96
+ {/* Terminal */}
97
+ <div className="flex-1 overflow-hidden">
98
+ <Terminal
99
+ ref={terminalRef}
100
+ onConnected={handleConnected}
101
+ showImageButton={false}
102
+ />
103
+ </div>
104
+ </div>
105
+ );
106
+ }
@@ -0,0 +1,55 @@
1
+ import { ThemeToggle } from "@/components/ThemeToggle";
2
+ import {
3
+ Tooltip,
4
+ TooltipContent,
5
+ TooltipTrigger,
6
+ } from "@/components/ui/tooltip";
7
+
8
+ export function SidebarFooter() {
9
+ return (
10
+ <div className="mt-auto px-3 pt-2 pb-[max(0.75rem,env(safe-area-inset-bottom))]">
11
+ <div className="flex items-center justify-between">
12
+ <span className="text-muted-foreground text-xs">Theme</span>
13
+ <ThemeToggle />
14
+ </div>
15
+ <div className="text-muted-foreground/50 mt-2 text-center text-[10px]">
16
+ <Tooltip>
17
+ <TooltipTrigger asChild>
18
+ <a
19
+ href="https://aterm.app"
20
+ target="_blank"
21
+ rel="noopener noreferrer"
22
+ className="hover:text-muted-foreground transition-colors"
23
+ >
24
+ aTerm
25
+ </a>
26
+ </TooltipTrigger>
27
+ <TooltipContent side="top">
28
+ <p>Desktop terminal workspace for AI coding agents</p>
29
+ </TooltipContent>
30
+ </Tooltip>
31
+ <span className="mx-1.5">·</span>
32
+ <Tooltip>
33
+ <TooltipTrigger asChild>
34
+ <a
35
+ href="https://lumifyhub.io"
36
+ target="_blank"
37
+ rel="noopener noreferrer"
38
+ className="hover:text-muted-foreground transition-colors"
39
+ >
40
+ LumifyHub
41
+ </a>
42
+ </TooltipTrigger>
43
+ <TooltipContent side="top">
44
+ <p className="flex items-center gap-1.5">
45
+ Team collaboration with chat and documentation
46
+ <span className="bg-primary/15 text-primary rounded-full px-1.5 py-0.5 text-[10px] font-medium">
47
+ Sponsor
48
+ </span>
49
+ </p>
50
+ </TooltipContent>
51
+ </Tooltip>
52
+ </div>
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,45 @@
1
+ "use client";
2
+
3
+ import { Keyboard, KeyboardOff } from "lucide-react";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ interface KeybarToggleButtonProps {
7
+ isVisible: boolean;
8
+ onToggle: () => void;
9
+ }
10
+
11
+ /**
12
+ * Floating button to toggle mobile keybar visibility.
13
+ * Positioned at bottom-right of terminal, above the keybar when visible.
14
+ */
15
+ export function KeybarToggleButton({
16
+ isVisible,
17
+ onToggle,
18
+ }: KeybarToggleButtonProps) {
19
+ return (
20
+ <button
21
+ onClick={onToggle}
22
+ className={cn(
23
+ "absolute right-3 z-30",
24
+ "flex items-center justify-center",
25
+ "h-11 w-11 rounded-full",
26
+ "bg-secondary/90 backdrop-blur-sm",
27
+ "shadow-lg",
28
+ "text-muted-foreground hover:bg-accent hover:text-foreground",
29
+ "touch-manipulation transition-all duration-200",
30
+ "active:scale-95",
31
+ // Position: moves up when keyboard is visible (accounts for safe-area + taller keys + recent commands bar)
32
+ isVisible
33
+ ? "bottom-[265px]"
34
+ : "bottom-[calc(1rem+env(safe-area-inset-bottom))]"
35
+ )}
36
+ aria-label={isVisible ? "Hide keyboard" : "Show keyboard"}
37
+ >
38
+ {isVisible ? (
39
+ <KeyboardOff className="h-5 w-5" />
40
+ ) : (
41
+ <Keyboard className="h-5 w-5" />
42
+ )}
43
+ </button>
44
+ );
45
+ }
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import { ArrowDown } from "lucide-react";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ interface ScrollToBottomButtonProps {
7
+ visible: boolean;
8
+ onClick: () => void;
9
+ }
10
+
11
+ export function ScrollToBottomButton({
12
+ visible,
13
+ onClick,
14
+ }: ScrollToBottomButtonProps) {
15
+ if (!visible) return null;
16
+
17
+ return (
18
+ <button
19
+ onClick={onClick}
20
+ className={cn(
21
+ "absolute right-6 bottom-6 p-3",
22
+ "bg-primary/90 hover:bg-primary backdrop-blur-sm",
23
+ "text-primary-foreground shadow-primary/30 rounded-full shadow-xl",
24
+ "transition-all hover:scale-105 active:scale-95",
25
+ "animate-bounce"
26
+ )}
27
+ title="Scroll to bottom"
28
+ >
29
+ <ArrowDown className="h-5 w-5" />
30
+ </button>
31
+ );
32
+ }
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ import { forwardRef } from "react";
4
+ import { Search, ChevronUp, ChevronDown, X } from "lucide-react";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ interface SearchBarProps {
8
+ visible: boolean;
9
+ query: string;
10
+ onQueryChange: (query: string) => void;
11
+ onFindNext: () => void;
12
+ onFindPrevious: () => void;
13
+ onClose: () => void;
14
+ }
15
+
16
+ export const SearchBar = forwardRef<HTMLInputElement, SearchBarProps>(
17
+ (
18
+ { visible, query, onQueryChange, onFindNext, onFindPrevious, onClose },
19
+ ref
20
+ ) => {
21
+ if (!visible) return null;
22
+
23
+ return (
24
+ <div
25
+ className={cn(
26
+ "flex items-center gap-2 px-4 py-2",
27
+ "bg-zinc-900/90 backdrop-blur-sm",
28
+ "border-b border-zinc-800"
29
+ )}
30
+ >
31
+ <Search className="h-4 w-4 text-zinc-500" />
32
+ <input
33
+ ref={ref}
34
+ type="text"
35
+ value={query}
36
+ onChange={(e) => onQueryChange(e.target.value)}
37
+ placeholder="Search in terminal..."
38
+ className={cn(
39
+ "flex-1 bg-transparent text-sm text-zinc-100 placeholder:text-zinc-500",
40
+ "focus:outline-none"
41
+ )}
42
+ />
43
+ <div className="flex items-center gap-1">
44
+ <button
45
+ onClick={onFindPrevious}
46
+ className="rounded p-1.5 text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-zinc-100"
47
+ title="Previous (Shift+Enter)"
48
+ >
49
+ <ChevronUp className="h-4 w-4" />
50
+ </button>
51
+ <button
52
+ onClick={onFindNext}
53
+ className="rounded p-1.5 text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-zinc-100"
54
+ title="Next (Enter)"
55
+ >
56
+ <ChevronDown className="h-4 w-4" />
57
+ </button>
58
+ <button
59
+ onClick={onClose}
60
+ className="rounded p-1.5 text-zinc-500 transition-colors hover:bg-zinc-800 hover:text-zinc-100"
61
+ title="Close (Esc)"
62
+ >
63
+ <X className="h-4 w-4" />
64
+ </button>
65
+ </div>
66
+ </div>
67
+ );
68
+ }
69
+ );
70
+
71
+ SearchBar.displayName = "SearchBar";