@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.
- package/LICENSE +21 -0
- package/README.md +123 -0
- package/app/api/claude/hidden/route.ts +66 -0
- package/app/api/claude/projects/[name]/sessions/route.ts +71 -0
- package/app/api/claude/projects/route.ts +44 -0
- package/app/api/code-search/available/route.ts +12 -0
- package/app/api/code-search/route.ts +47 -0
- package/app/api/dev-servers/[id]/logs/route.ts +23 -0
- package/app/api/dev-servers/[id]/restart/route.ts +20 -0
- package/app/api/dev-servers/[id]/route.ts +51 -0
- package/app/api/dev-servers/[id]/stop/route.ts +20 -0
- package/app/api/dev-servers/detect/route.ts +39 -0
- package/app/api/dev-servers/route.ts +48 -0
- package/app/api/exec/route.ts +60 -0
- package/app/api/files/content/route.ts +76 -0
- package/app/api/files/route.ts +37 -0
- package/app/api/files/upload-temp/route.ts +41 -0
- package/app/api/git/check/route.ts +54 -0
- package/app/api/git/clone/route.ts +99 -0
- package/app/api/git/commit/route.ts +75 -0
- package/app/api/git/discard/route.ts +38 -0
- package/app/api/git/file-content/route.ts +64 -0
- package/app/api/git/history/[hash]/diff/route.ts +38 -0
- package/app/api/git/history/[hash]/route.ts +34 -0
- package/app/api/git/history/route.ts +27 -0
- package/app/api/git/multi-status/route.ts +46 -0
- package/app/api/git/pr/route.ts +164 -0
- package/app/api/git/push/route.ts +64 -0
- package/app/api/git/stage/route.ts +40 -0
- package/app/api/git/status/route.ts +51 -0
- package/app/api/git/unstage/route.ts +46 -0
- package/app/api/groups/[...path]/route.ts +136 -0
- package/app/api/groups/route.ts +93 -0
- package/app/api/orchestrate/spawn/route.ts +45 -0
- package/app/api/orchestrate/workers/[id]/route.ts +89 -0
- package/app/api/orchestrate/workers/route.ts +31 -0
- package/app/api/projects/[id]/detect/route.ts +27 -0
- package/app/api/projects/[id]/dev-servers/[dsId]/route.ts +66 -0
- package/app/api/projects/[id]/dev-servers/route.ts +51 -0
- package/app/api/projects/[id]/repositories/[repoId]/route.ts +67 -0
- package/app/api/projects/[id]/repositories/route.ts +74 -0
- package/app/api/projects/[id]/route.ts +108 -0
- package/app/api/projects/detect/route.ts +33 -0
- package/app/api/projects/route.ts +59 -0
- package/app/api/sessions/[id]/claude-session/route.ts +42 -0
- package/app/api/sessions/[id]/fork/route.ts +74 -0
- package/app/api/sessions/[id]/mcp-config/route.ts +34 -0
- package/app/api/sessions/[id]/messages/route.ts +60 -0
- package/app/api/sessions/[id]/pr/route.ts +188 -0
- package/app/api/sessions/[id]/preview/route.ts +42 -0
- package/app/api/sessions/[id]/route.ts +229 -0
- package/app/api/sessions/[id]/send-keys/route.ts +119 -0
- package/app/api/sessions/[id]/summarize/route.ts +331 -0
- package/app/api/sessions/init-script/route.ts +84 -0
- package/app/api/sessions/route.ts +209 -0
- package/app/api/sessions/status/route.ts +237 -0
- package/app/api/system/route.ts +9 -0
- package/app/api/tmux/kill-all/route.ts +57 -0
- package/app/api/tmux/rename/route.ts +30 -0
- package/app/globals.css +174 -0
- package/app/icon.svg +11 -0
- package/app/layout.tsx +122 -0
- package/app/page.tsx +629 -0
- package/components/ChatMessage.tsx +65 -0
- package/components/ChatView.tsx +276 -0
- package/components/ClaudeProjects/ClaudeProjectCard.tsx +195 -0
- package/components/ClaudeProjects/ClaudeProjectsSection.tsx +89 -0
- package/components/ClaudeProjects/ClaudeSessionCard.tsx +100 -0
- package/components/ClaudeProjects/index.ts +1 -0
- package/components/CodeSearch/CodeSearchResults.tsx +177 -0
- package/components/ConductorPanel.tsx +256 -0
- package/components/DevServers/DevServerCard.tsx +311 -0
- package/components/DevServers/DevServersSection.tsx +91 -0
- package/components/DevServers/ServerLogsModal.tsx +151 -0
- package/components/DevServers/StartServerDialog.tsx +359 -0
- package/components/DevServers/index.ts +4 -0
- package/components/DiffViewer/DiffModal.tsx +151 -0
- package/components/DiffViewer/UnifiedDiff.tsx +185 -0
- package/components/DiffViewer/index.tsx +2 -0
- package/components/DirectoryPicker.tsx +355 -0
- package/components/FileExplorer/FileEditor.tsx +276 -0
- package/components/FileExplorer/FileTabs.tsx +118 -0
- package/components/FileExplorer/FileTree.tsx +214 -0
- package/components/FileExplorer/HtmlRenderer.tsx +16 -0
- package/components/FileExplorer/MarkdownRenderer.tsx +18 -0
- package/components/FileExplorer/index.tsx +520 -0
- package/components/FilePicker.tsx +339 -0
- package/components/FolderPicker.tsx +201 -0
- package/components/GitDrawer/FileEditDialog.tsx +400 -0
- package/components/GitDrawer/index.tsx +464 -0
- package/components/GitPanel/CommitForm.tsx +205 -0
- package/components/GitPanel/CommitHistory.tsx +174 -0
- package/components/GitPanel/CommitItem.tsx +196 -0
- package/components/GitPanel/FileChanges.tsx +414 -0
- package/components/GitPanel/GitPanelTabs.tsx +39 -0
- package/components/GitPanel/index.tsx +817 -0
- package/components/MessageInput.tsx +82 -0
- package/components/NewClaudeSessionDialog.tsx +166 -0
- package/components/NewSessionDialog/AdvancedSettings.tsx +78 -0
- package/components/NewSessionDialog/AgentSelector.tsx +37 -0
- package/components/NewSessionDialog/CreatingOverlay.tsx +94 -0
- package/components/NewSessionDialog/NewSessionDialog.types.ts +136 -0
- package/components/NewSessionDialog/ProjectSelector.tsx +146 -0
- package/components/NewSessionDialog/WorkingDirectoryInput.tsx +55 -0
- package/components/NewSessionDialog/WorktreeSection.tsx +92 -0
- package/components/NewSessionDialog/hooks/useNewSessionForm.ts +370 -0
- package/components/NewSessionDialog/index.tsx +106 -0
- package/components/NotificationSettings.tsx +127 -0
- package/components/PRCreationModal.tsx +272 -0
- package/components/Pane/DesktopTabBar.tsx +353 -0
- package/components/Pane/MobileTabBar.tsx +210 -0
- package/components/Pane/OpenInVSCode.tsx +69 -0
- package/components/Pane/PaneSkeletons.tsx +57 -0
- package/components/Pane/index.tsx +558 -0
- package/components/PaneLayout.tsx +60 -0
- package/components/Projects/DevServersSection.tsx +140 -0
- package/components/Projects/DirectoryField.tsx +92 -0
- package/components/Projects/NewProjectDialog.tsx +188 -0
- package/components/Projects/NewProjectDialog.types.ts +46 -0
- package/components/Projects/ProjectCard.tsx +276 -0
- package/components/Projects/ProjectSettingsDialog.tsx +811 -0
- package/components/Projects/hooks/useNewProjectForm.ts +249 -0
- package/components/Projects/index.ts +3 -0
- package/components/Providers.tsx +49 -0
- package/components/QuickSwitcher.tsx +306 -0
- package/components/SessionList/KillAllConfirm.tsx +46 -0
- package/components/SessionList/SelectionToolbar.tsx +164 -0
- package/components/SessionList/SessionList.types.ts +37 -0
- package/components/SessionList/SessionListHeader.tsx +71 -0
- package/components/SessionList/hooks/useSessionListMutations.ts +269 -0
- package/components/SessionList/index.tsx +189 -0
- package/components/ShellDrawer/index.tsx +106 -0
- package/components/SidebarFooter.tsx +55 -0
- package/components/Terminal/KeybarToggleButton.tsx +45 -0
- package/components/Terminal/ScrollToBottomButton.tsx +32 -0
- package/components/Terminal/SearchBar.tsx +71 -0
- package/components/Terminal/TerminalToolbar.tsx +551 -0
- package/components/Terminal/VirtualKeyboard.tsx +711 -0
- package/components/Terminal/constants.ts +20 -0
- package/components/Terminal/hooks/index.ts +5 -0
- package/components/Terminal/hooks/resize-handlers.ts +140 -0
- package/components/Terminal/hooks/terminal-init.ts +151 -0
- package/components/Terminal/hooks/touch-scroll.ts +155 -0
- package/components/Terminal/hooks/useTerminalConnection.ts +282 -0
- package/components/Terminal/hooks/useTerminalConnection.types.ts +39 -0
- package/components/Terminal/hooks/useTerminalSearch.ts +103 -0
- package/components/Terminal/hooks/websocket-connection.ts +274 -0
- package/components/Terminal/index.tsx +320 -0
- package/components/ThemeToggle.tsx +168 -0
- package/components/TmuxSessions.tsx +132 -0
- package/components/ToolCallDisplay.tsx +71 -0
- package/components/WorkerCard.tsx +245 -0
- package/components/a/ABadge.tsx +115 -0
- package/components/a/AButton.tsx +163 -0
- package/components/a/ADialog.tsx +93 -0
- package/components/a/ADropdownMenu.tsx +279 -0
- package/components/a/AIconButton.tsx +190 -0
- package/components/a/ASheet.tsx +150 -0
- package/components/a/ATooltip.tsx +77 -0
- package/components/a/index.ts +64 -0
- package/components/mobile/SwipeSidebar.tsx +122 -0
- package/components/ui/badge.tsx +41 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/context-menu.tsx +197 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/input.tsx +21 -0
- package/components/ui/scroll-area.tsx +52 -0
- package/components/ui/select.tsx +159 -0
- package/components/ui/skeleton.tsx +111 -0
- package/components/ui/switch.tsx +31 -0
- package/components/ui/textarea.tsx +21 -0
- package/components/ui/tooltip.tsx +32 -0
- package/components/views/DesktopView.tsx +244 -0
- package/components/views/MobileView.tsx +110 -0
- package/components/views/types.ts +75 -0
- package/contexts/PaneContext.tsx +336 -0
- package/data/claude/index.ts +9 -0
- package/data/claude/keys.ts +6 -0
- package/data/claude/queries.ts +120 -0
- package/data/claude/useClaudeUpdates.ts +37 -0
- package/data/code-search/index.ts +2 -0
- package/data/code-search/keys.ts +7 -0
- package/data/code-search/queries.ts +61 -0
- package/data/dev-servers/index.ts +8 -0
- package/data/dev-servers/keys.ts +4 -0
- package/data/dev-servers/queries.ts +104 -0
- package/data/files/index.ts +3 -0
- package/data/files/keys.ts +4 -0
- package/data/files/queries.ts +25 -0
- package/data/git/keys.ts +15 -0
- package/data/git/queries.ts +395 -0
- package/data/groups/index.ts +1 -0
- package/data/groups/mutations.ts +95 -0
- package/data/projects/index.ts +10 -0
- package/data/projects/keys.ts +4 -0
- package/data/projects/queries.ts +193 -0
- package/data/repositories/index.ts +7 -0
- package/data/repositories/keys.ts +5 -0
- package/data/repositories/queries.ts +122 -0
- package/data/sessions/index.ts +12 -0
- package/data/sessions/keys.ts +8 -0
- package/data/sessions/queries.ts +218 -0
- package/data/statuses/index.ts +1 -0
- package/data/statuses/queries.ts +69 -0
- package/hooks/useCopyToClipboard.ts +48 -0
- package/hooks/useDevServersManager.ts +73 -0
- package/hooks/useDirectoryBrowser.ts +90 -0
- package/hooks/useDrawerAnimation.ts +27 -0
- package/hooks/useFileDrop.ts +87 -0
- package/hooks/useFileEditor.ts +184 -0
- package/hooks/useGroups.ts +37 -0
- package/hooks/useHomePath.ts +34 -0
- package/hooks/useKeyRepeat.ts +55 -0
- package/hooks/useKeybarVisibility.ts +42 -0
- package/hooks/useNotifications.ts +257 -0
- package/hooks/useProjects.ts +53 -0
- package/hooks/useSessionStatuses.ts +30 -0
- package/hooks/useSessions.ts +86 -0
- package/hooks/useSpeechRecognition.ts +124 -0
- package/hooks/useViewport.ts +32 -0
- package/hooks/useViewportHeight.ts +50 -0
- package/lib/async-operations.ts +35 -0
- package/lib/banner.ts +81 -0
- package/lib/claude/jsonl-cache.ts +86 -0
- package/lib/claude/jsonl-reader.ts +271 -0
- package/lib/claude/process-manager.ts +278 -0
- package/lib/claude/stream-parser.ts +173 -0
- package/lib/claude/types.ts +154 -0
- package/lib/claude/watcher.ts +71 -0
- package/lib/client/session-registry.ts +111 -0
- package/lib/code-search.ts +121 -0
- package/lib/db/index.ts +48 -0
- package/lib/db/migrations.ts +45 -0
- package/lib/db/queries.ts +460 -0
- package/lib/db/schema.ts +114 -0
- package/lib/db/types.ts +92 -0
- package/lib/db.ts +2 -0
- package/lib/dev-servers.ts +509 -0
- package/lib/diff-parser.ts +221 -0
- package/lib/env-setup.ts +285 -0
- package/lib/file-upload.ts +34 -0
- package/lib/file-utils.ts +50 -0
- package/lib/files.ts +207 -0
- package/lib/git-history.ts +294 -0
- package/lib/git-status.ts +391 -0
- package/lib/git.ts +257 -0
- package/lib/mcp-config.ts +81 -0
- package/lib/multi-repo-git.ts +179 -0
- package/lib/notifications.ts +219 -0
- package/lib/orchestration.ts +448 -0
- package/lib/panes.ts +232 -0
- package/lib/ports.ts +97 -0
- package/lib/pr-generation.ts +307 -0
- package/lib/pr.ts +234 -0
- package/lib/projects.ts +578 -0
- package/lib/providers/registry.ts +70 -0
- package/lib/providers.ts +121 -0
- package/lib/query-client.ts +14 -0
- package/lib/rangeSelectionUtils.ts +65 -0
- package/lib/status-detector.ts +375 -0
- package/lib/terminal-themes.ts +265 -0
- package/lib/theme-config.ts +327 -0
- package/lib/utils.ts +6 -0
- package/lib/worktrees.ts +262 -0
- package/mcp/orchestration-server.ts +438 -0
- package/package.json +139 -0
- package/postcss.config.mjs +7 -0
- package/public/icon.svg +10 -0
- package/public/icons/icon-128x128.png +0 -0
- package/public/icons/icon-144x144.png +0 -0
- package/public/icons/icon-152x152.png +0 -0
- package/public/icons/icon-192x192.png +0 -0
- package/public/icons/icon-384x384.png +0 -0
- package/public/icons/icon-512x512.png +0 -0
- package/public/icons/icon-72x72.png +0 -0
- package/public/icons/icon-96x96.png +0 -0
- package/public/manifest.json +61 -0
- package/public/sw.js +64 -0
- package/scripts/agent-os +91 -0
- package/scripts/install.sh +48 -0
- package/scripts/lib/ai-clis.sh +132 -0
- package/scripts/lib/commands.sh +487 -0
- package/scripts/lib/common.sh +89 -0
- package/scripts/lib/prerequisites.sh +462 -0
- package/scripts/setup.sh +134 -0
- package/server.ts +155 -0
- package/stores/fileOpen.ts +26 -0
- package/stores/index.ts +1 -0
- package/stores/initialPrompt.ts +24 -0
- package/stores/sessionSelection.ts +48 -0
- package/styles/themes.css +603 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useCallback, useState } from "react";
|
|
4
|
+
import { useSnapshot } from "valtio";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { Trash2, X } from "lucide-react";
|
|
7
|
+
import { selectionStore, selectionActions } from "@/stores/sessionSelection";
|
|
8
|
+
import {
|
|
9
|
+
Dialog,
|
|
10
|
+
DialogContent,
|
|
11
|
+
DialogDescription,
|
|
12
|
+
DialogFooter,
|
|
13
|
+
DialogHeader,
|
|
14
|
+
DialogTitle,
|
|
15
|
+
} from "@/components/ui/dialog";
|
|
16
|
+
import {
|
|
17
|
+
Tooltip,
|
|
18
|
+
TooltipContent,
|
|
19
|
+
TooltipTrigger,
|
|
20
|
+
} from "@/components/ui/tooltip";
|
|
21
|
+
|
|
22
|
+
interface SelectionToolbarProps {
|
|
23
|
+
allSessionIds: string[];
|
|
24
|
+
onDeleteSessions: (sessionIds: string[]) => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function SelectionToolbar({
|
|
28
|
+
allSessionIds,
|
|
29
|
+
onDeleteSessions,
|
|
30
|
+
}: SelectionToolbarProps) {
|
|
31
|
+
const { selectedIds } = useSnapshot(selectionStore);
|
|
32
|
+
const selectedCount = selectedIds.size;
|
|
33
|
+
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
|
34
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
35
|
+
|
|
36
|
+
const handleSelectAll = useCallback(() => {
|
|
37
|
+
selectionActions.selectAll(allSessionIds);
|
|
38
|
+
}, [allSessionIds]);
|
|
39
|
+
|
|
40
|
+
const handleDelete = useCallback(async () => {
|
|
41
|
+
const ids = selectionActions.getSelectedIds();
|
|
42
|
+
if (ids.length === 0) return;
|
|
43
|
+
|
|
44
|
+
setIsDeleting(true);
|
|
45
|
+
try {
|
|
46
|
+
await onDeleteSessions(ids);
|
|
47
|
+
selectionActions.clear();
|
|
48
|
+
} finally {
|
|
49
|
+
setIsDeleting(false);
|
|
50
|
+
setShowDeleteDialog(false);
|
|
51
|
+
}
|
|
52
|
+
}, [onDeleteSessions]);
|
|
53
|
+
|
|
54
|
+
// Keyboard shortcuts
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (selectedCount === 0) return;
|
|
57
|
+
|
|
58
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
59
|
+
// Delete key - show delete confirmation
|
|
60
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
61
|
+
// Don't trigger if typing in an input
|
|
62
|
+
if (
|
|
63
|
+
e.target instanceof HTMLInputElement ||
|
|
64
|
+
e.target instanceof HTMLTextAreaElement
|
|
65
|
+
) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
setShowDeleteDialog(true);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Escape - clear selection
|
|
73
|
+
if (e.key === "Escape") {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
selectionActions.clear();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
80
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
81
|
+
}, [selectedCount]);
|
|
82
|
+
|
|
83
|
+
if (selectedCount === 0) return null;
|
|
84
|
+
|
|
85
|
+
const allSelected = selectedCount === allSessionIds.length;
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
<div className="bg-primary/10 border-primary/20 flex items-center gap-2 border-b px-3 py-2">
|
|
90
|
+
<span className="text-sm font-medium whitespace-nowrap">
|
|
91
|
+
{selectedCount} selected
|
|
92
|
+
</span>
|
|
93
|
+
<div className="ml-auto flex items-center gap-1">
|
|
94
|
+
{!allSelected && (
|
|
95
|
+
<Button
|
|
96
|
+
variant="ghost"
|
|
97
|
+
size="sm"
|
|
98
|
+
className="h-7 text-xs"
|
|
99
|
+
onClick={handleSelectAll}
|
|
100
|
+
>
|
|
101
|
+
Select all
|
|
102
|
+
</Button>
|
|
103
|
+
)}
|
|
104
|
+
<Tooltip>
|
|
105
|
+
<TooltipTrigger asChild>
|
|
106
|
+
<Button
|
|
107
|
+
variant="ghost"
|
|
108
|
+
size="icon-sm"
|
|
109
|
+
className="text-destructive hover:text-destructive hover:bg-destructive/10 h-6 w-6"
|
|
110
|
+
onClick={() => setShowDeleteDialog(true)}
|
|
111
|
+
>
|
|
112
|
+
<Trash2 className="h-3 w-3" />
|
|
113
|
+
</Button>
|
|
114
|
+
</TooltipTrigger>
|
|
115
|
+
<TooltipContent>Delete selected</TooltipContent>
|
|
116
|
+
</Tooltip>
|
|
117
|
+
<Tooltip>
|
|
118
|
+
<TooltipTrigger asChild>
|
|
119
|
+
<Button
|
|
120
|
+
variant="ghost"
|
|
121
|
+
size="icon-sm"
|
|
122
|
+
className="h-6 w-6"
|
|
123
|
+
onClick={selectionActions.clear}
|
|
124
|
+
>
|
|
125
|
+
<X className="h-3 w-3" />
|
|
126
|
+
</Button>
|
|
127
|
+
</TooltipTrigger>
|
|
128
|
+
<TooltipContent>Clear selection</TooltipContent>
|
|
129
|
+
</Tooltip>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<Dialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
|
134
|
+
<DialogContent showCloseButton={false}>
|
|
135
|
+
<DialogHeader>
|
|
136
|
+
<DialogTitle>
|
|
137
|
+
Delete {selectedCount} session{selectedCount > 1 ? "s" : ""}?
|
|
138
|
+
</DialogTitle>
|
|
139
|
+
<DialogDescription>
|
|
140
|
+
This will permanently delete the selected sessions and their tmux
|
|
141
|
+
sessions. This action cannot be undone.
|
|
142
|
+
</DialogDescription>
|
|
143
|
+
</DialogHeader>
|
|
144
|
+
<DialogFooter>
|
|
145
|
+
<Button
|
|
146
|
+
variant="ghost"
|
|
147
|
+
onClick={() => setShowDeleteDialog(false)}
|
|
148
|
+
disabled={isDeleting}
|
|
149
|
+
>
|
|
150
|
+
Cancel
|
|
151
|
+
</Button>
|
|
152
|
+
<Button
|
|
153
|
+
variant="destructive"
|
|
154
|
+
onClick={handleDelete}
|
|
155
|
+
disabled={isDeleting}
|
|
156
|
+
>
|
|
157
|
+
{isDeleting ? "Deleting..." : "Delete"}
|
|
158
|
+
</Button>
|
|
159
|
+
</DialogFooter>
|
|
160
|
+
</DialogContent>
|
|
161
|
+
</Dialog>
|
|
162
|
+
</>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Session, Group } from "@/lib/db";
|
|
2
|
+
|
|
3
|
+
export interface SessionStatus {
|
|
4
|
+
sessionName: string;
|
|
5
|
+
status: "idle" | "running" | "waiting" | "error" | "dead";
|
|
6
|
+
lastLine?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SessionListProps {
|
|
10
|
+
activeSessionId?: string;
|
|
11
|
+
sessionStatuses?: Record<string, SessionStatus>;
|
|
12
|
+
onSelect: (sessionId: string) => void;
|
|
13
|
+
onOpenInTab?: (sessionId: string) => void;
|
|
14
|
+
onNewSessionInProject?: (projectId: string) => void;
|
|
15
|
+
onOpenTerminal?: (projectId: string) => void;
|
|
16
|
+
onStartDevServer?: (projectId: string) => void;
|
|
17
|
+
onResumeClaudeSession?: (
|
|
18
|
+
claudeSessionId: string,
|
|
19
|
+
cwd: string,
|
|
20
|
+
summary?: string,
|
|
21
|
+
projectName?: string
|
|
22
|
+
) => void;
|
|
23
|
+
onNewSession?: (cwd?: string, projectName?: string) => void;
|
|
24
|
+
onCreateDevServer?: (opts: {
|
|
25
|
+
projectId: string;
|
|
26
|
+
type: "node" | "docker";
|
|
27
|
+
name: string;
|
|
28
|
+
command: string;
|
|
29
|
+
workingDirectory: string;
|
|
30
|
+
ports?: number[];
|
|
31
|
+
}) => Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface SessionHoverHandlers {
|
|
35
|
+
onHoverStart: (session: Session, rect: DOMRect) => void;
|
|
36
|
+
onHoverEnd: () => void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ADropdownMenu, menuItem } from "@/components/a/ADropdownMenu";
|
|
2
|
+
import {
|
|
3
|
+
Plus,
|
|
4
|
+
FolderPlus,
|
|
5
|
+
FolderOpen,
|
|
6
|
+
GitBranch,
|
|
7
|
+
MoreHorizontal,
|
|
8
|
+
Trash2,
|
|
9
|
+
} from "lucide-react";
|
|
10
|
+
|
|
11
|
+
interface SessionListHeaderProps {
|
|
12
|
+
onNewProject: () => void;
|
|
13
|
+
onOpenProject: () => void;
|
|
14
|
+
onCloneFromGithub: () => void;
|
|
15
|
+
onKillAll: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function SessionListHeader({
|
|
19
|
+
onNewProject,
|
|
20
|
+
onOpenProject,
|
|
21
|
+
onCloneFromGithub,
|
|
22
|
+
onKillAll,
|
|
23
|
+
}: SessionListHeaderProps) {
|
|
24
|
+
return (
|
|
25
|
+
<div className="flex items-center justify-between px-3 py-2">
|
|
26
|
+
<div className="flex items-center gap-2">
|
|
27
|
+
<svg
|
|
28
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
29
|
+
viewBox="0 0 24 24"
|
|
30
|
+
className="h-5 w-5"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
strokeWidth="2"
|
|
33
|
+
strokeLinecap="round"
|
|
34
|
+
strokeLinejoin="round"
|
|
35
|
+
fill="none"
|
|
36
|
+
>
|
|
37
|
+
<path d="M12 8V4H8" />
|
|
38
|
+
<rect width="16" height="12" x="4" y="8" rx="2" />
|
|
39
|
+
<path d="M2 14h2" />
|
|
40
|
+
<path d="M20 14h2" />
|
|
41
|
+
<path d="M15 13v2" />
|
|
42
|
+
<path d="M9 13v2" />
|
|
43
|
+
</svg>
|
|
44
|
+
<h2 className="font-semibold">ClaudeDeck</h2>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="flex gap-1">
|
|
47
|
+
<ADropdownMenu
|
|
48
|
+
icon={Plus}
|
|
49
|
+
tooltip="New project"
|
|
50
|
+
items={[
|
|
51
|
+
menuItem("New Project", onNewProject, { icon: FolderPlus }),
|
|
52
|
+
menuItem("Open Project", onOpenProject, { icon: FolderOpen }),
|
|
53
|
+
menuItem("Clone from GitHub", onCloneFromGithub, {
|
|
54
|
+
icon: GitBranch,
|
|
55
|
+
}),
|
|
56
|
+
]}
|
|
57
|
+
/>
|
|
58
|
+
<ADropdownMenu
|
|
59
|
+
icon={MoreHorizontal}
|
|
60
|
+
tooltip="More options"
|
|
61
|
+
items={[
|
|
62
|
+
menuItem("Kill all sessions", onKillAll, {
|
|
63
|
+
icon: Trash2,
|
|
64
|
+
variant: "destructive",
|
|
65
|
+
}),
|
|
66
|
+
]}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { toast } from "sonner";
|
|
4
|
+
import {
|
|
5
|
+
useDeleteSession,
|
|
6
|
+
useRenameSession,
|
|
7
|
+
useForkSession,
|
|
8
|
+
useSummarizeSession,
|
|
9
|
+
useMoveSessionToProject,
|
|
10
|
+
} from "@/data/sessions";
|
|
11
|
+
import {
|
|
12
|
+
useToggleProject,
|
|
13
|
+
useDeleteProject,
|
|
14
|
+
useRenameProject,
|
|
15
|
+
} from "@/data/projects";
|
|
16
|
+
import { useToggleGroup, useCreateGroup, useDeleteGroup } from "@/data/groups";
|
|
17
|
+
import {
|
|
18
|
+
useStopDevServer,
|
|
19
|
+
useRestartDevServer,
|
|
20
|
+
useRemoveDevServer,
|
|
21
|
+
} from "@/data/dev-servers";
|
|
22
|
+
import { sessionKeys } from "@/data/sessions/keys";
|
|
23
|
+
|
|
24
|
+
interface UseSessionListMutationsOptions {
|
|
25
|
+
onSelectSession: (sessionId: string) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useSessionListMutations({
|
|
29
|
+
onSelectSession,
|
|
30
|
+
}: UseSessionListMutationsOptions) {
|
|
31
|
+
const queryClient = useQueryClient();
|
|
32
|
+
|
|
33
|
+
// Session mutations
|
|
34
|
+
const deleteSessionMutation = useDeleteSession();
|
|
35
|
+
const renameSessionMutation = useRenameSession();
|
|
36
|
+
const forkSessionMutation = useForkSession();
|
|
37
|
+
const summarizeSessionMutation = useSummarizeSession();
|
|
38
|
+
const moveSessionToProjectMutation = useMoveSessionToProject();
|
|
39
|
+
|
|
40
|
+
// Project mutations
|
|
41
|
+
const toggleProjectMutation = useToggleProject();
|
|
42
|
+
const deleteProjectMutation = useDeleteProject();
|
|
43
|
+
const renameProjectMutation = useRenameProject();
|
|
44
|
+
|
|
45
|
+
// Group mutations
|
|
46
|
+
const toggleGroupMutation = useToggleGroup();
|
|
47
|
+
const createGroupMutation = useCreateGroup();
|
|
48
|
+
const deleteGroupMutation = useDeleteGroup();
|
|
49
|
+
|
|
50
|
+
// Dev server mutations
|
|
51
|
+
const stopDevServerMutation = useStopDevServer();
|
|
52
|
+
const restartDevServerMutation = useRestartDevServer();
|
|
53
|
+
const removeDevServerMutation = useRemoveDevServer();
|
|
54
|
+
|
|
55
|
+
// Derived state
|
|
56
|
+
const summarizingSessionId = summarizeSessionMutation.isPending
|
|
57
|
+
? (summarizeSessionMutation.variables as string)
|
|
58
|
+
: null;
|
|
59
|
+
|
|
60
|
+
// Session handlers
|
|
61
|
+
const handleDeleteSession = useCallback(
|
|
62
|
+
async (sessionId: string) => {
|
|
63
|
+
if (!confirm("Delete this session? This cannot be undone.")) return;
|
|
64
|
+
await deleteSessionMutation.mutateAsync(sessionId);
|
|
65
|
+
},
|
|
66
|
+
[deleteSessionMutation]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const handleRenameSession = useCallback(
|
|
70
|
+
async (sessionId: string, newName: string) => {
|
|
71
|
+
await renameSessionMutation.mutateAsync({ sessionId, newName });
|
|
72
|
+
},
|
|
73
|
+
[renameSessionMutation]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const handleForkSession = useCallback(
|
|
77
|
+
async (sessionId: string) => {
|
|
78
|
+
const forkedSession = await forkSessionMutation.mutateAsync(sessionId);
|
|
79
|
+
if (forkedSession) onSelectSession(forkedSession.id);
|
|
80
|
+
},
|
|
81
|
+
[forkSessionMutation, onSelectSession]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const handleSummarize = useCallback(
|
|
85
|
+
async (sessionId: string) => {
|
|
86
|
+
const newSession = await summarizeSessionMutation.mutateAsync(sessionId);
|
|
87
|
+
if (newSession) onSelectSession(newSession.id);
|
|
88
|
+
},
|
|
89
|
+
[summarizeSessionMutation, onSelectSession]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const handleMoveSessionToProject = useCallback(
|
|
93
|
+
async (sessionId: string, projectId: string) => {
|
|
94
|
+
await moveSessionToProjectMutation.mutateAsync({ sessionId, projectId });
|
|
95
|
+
},
|
|
96
|
+
[moveSessionToProjectMutation]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Project handlers
|
|
100
|
+
const handleToggleProject = useCallback(
|
|
101
|
+
async (projectId: string, expanded: boolean) => {
|
|
102
|
+
await toggleProjectMutation.mutateAsync({ projectId, expanded });
|
|
103
|
+
},
|
|
104
|
+
[toggleProjectMutation]
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const handleDeleteProject = useCallback(
|
|
108
|
+
async (projectId: string) => {
|
|
109
|
+
if (
|
|
110
|
+
!confirm(
|
|
111
|
+
"Delete this project? Sessions will be moved to Uncategorized."
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
return;
|
|
115
|
+
await deleteProjectMutation.mutateAsync(projectId);
|
|
116
|
+
},
|
|
117
|
+
[deleteProjectMutation]
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const handleRenameProject = useCallback(
|
|
121
|
+
async (projectId: string, newName: string) => {
|
|
122
|
+
await renameProjectMutation.mutateAsync({ projectId, newName });
|
|
123
|
+
},
|
|
124
|
+
[renameProjectMutation]
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Group handlers
|
|
128
|
+
const handleToggleGroup = useCallback(
|
|
129
|
+
async (path: string, expanded: boolean) => {
|
|
130
|
+
await toggleGroupMutation.mutateAsync({ path, expanded });
|
|
131
|
+
},
|
|
132
|
+
[toggleGroupMutation]
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const handleCreateGroup = useCallback(
|
|
136
|
+
async (name: string, parentPath?: string) => {
|
|
137
|
+
await createGroupMutation.mutateAsync({ name, parentPath });
|
|
138
|
+
},
|
|
139
|
+
[createGroupMutation]
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const handleDeleteGroup = useCallback(
|
|
143
|
+
async (path: string) => {
|
|
144
|
+
if (!confirm("Delete this group? Sessions will be moved to parent."))
|
|
145
|
+
return;
|
|
146
|
+
await deleteGroupMutation.mutateAsync(path);
|
|
147
|
+
},
|
|
148
|
+
[deleteGroupMutation]
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Dev server handlers
|
|
152
|
+
const handleStopDevServer = useCallback(
|
|
153
|
+
async (serverId: string) => {
|
|
154
|
+
await stopDevServerMutation.mutateAsync(serverId);
|
|
155
|
+
},
|
|
156
|
+
[stopDevServerMutation]
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const handleRestartDevServer = useCallback(
|
|
160
|
+
async (serverId: string) => {
|
|
161
|
+
await restartDevServerMutation.mutateAsync(serverId);
|
|
162
|
+
},
|
|
163
|
+
[restartDevServerMutation]
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const handleRemoveDevServer = useCallback(
|
|
167
|
+
async (serverId: string) => {
|
|
168
|
+
await removeDevServerMutation.mutateAsync(serverId);
|
|
169
|
+
},
|
|
170
|
+
[removeDevServerMutation]
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Bulk delete handler
|
|
174
|
+
const handleBulkDelete = useCallback(
|
|
175
|
+
async (sessionIds: string[]) => {
|
|
176
|
+
const count = sessionIds.length;
|
|
177
|
+
const hasWorktrees = sessionIds.length > 0; // Assume some might have worktrees
|
|
178
|
+
|
|
179
|
+
// Show toast with progress
|
|
180
|
+
const toastId = toast.loading(
|
|
181
|
+
hasWorktrees
|
|
182
|
+
? `Deleting ${count} session${count > 1 ? "s" : ""}... cleaning up worktrees in background`
|
|
183
|
+
: `Deleting ${count} session${count > 1 ? "s" : ""}...`
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
let succeeded = 0;
|
|
187
|
+
let failed = 0;
|
|
188
|
+
|
|
189
|
+
// Delete all sessions in parallel for speed
|
|
190
|
+
await Promise.allSettled(
|
|
191
|
+
sessionIds.map(async (sessionId) => {
|
|
192
|
+
try {
|
|
193
|
+
const response = await fetch(`/api/sessions/${sessionId}`, {
|
|
194
|
+
method: "DELETE",
|
|
195
|
+
});
|
|
196
|
+
if (response.ok) {
|
|
197
|
+
succeeded++;
|
|
198
|
+
} else {
|
|
199
|
+
failed++;
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`Failed to delete session ${sessionId}:`, error);
|
|
203
|
+
failed++;
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Invalidate cache to refresh UI
|
|
209
|
+
queryClient.invalidateQueries({ queryKey: sessionKeys.list() });
|
|
210
|
+
|
|
211
|
+
// Update toast based on results
|
|
212
|
+
if (failed === 0) {
|
|
213
|
+
toast.success(
|
|
214
|
+
`Deleted ${succeeded} session${succeeded > 1 ? "s" : ""}`,
|
|
215
|
+
{ id: toastId }
|
|
216
|
+
);
|
|
217
|
+
} else if (succeeded === 0) {
|
|
218
|
+
toast.error(
|
|
219
|
+
`Failed to delete ${failed} session${failed > 1 ? "s" : ""}`,
|
|
220
|
+
{
|
|
221
|
+
id: toastId,
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
} else {
|
|
225
|
+
toast.warning(
|
|
226
|
+
`Deleted ${succeeded}, failed ${failed} session${failed > 1 ? "s" : ""}`,
|
|
227
|
+
{ id: toastId }
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
[queryClient]
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Refresh handler
|
|
235
|
+
const handleRefresh = useCallback(async () => {
|
|
236
|
+
await queryClient.invalidateQueries({ queryKey: sessionKeys.list() });
|
|
237
|
+
}, [queryClient]);
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
// Derived state
|
|
241
|
+
summarizingSessionId,
|
|
242
|
+
|
|
243
|
+
// Session handlers
|
|
244
|
+
handleDeleteSession,
|
|
245
|
+
handleRenameSession,
|
|
246
|
+
handleForkSession,
|
|
247
|
+
handleSummarize,
|
|
248
|
+
handleMoveSessionToProject,
|
|
249
|
+
|
|
250
|
+
// Project handlers
|
|
251
|
+
handleToggleProject,
|
|
252
|
+
handleDeleteProject,
|
|
253
|
+
handleRenameProject,
|
|
254
|
+
|
|
255
|
+
// Group handlers
|
|
256
|
+
handleToggleGroup,
|
|
257
|
+
handleCreateGroup,
|
|
258
|
+
handleDeleteGroup,
|
|
259
|
+
|
|
260
|
+
// Dev server handlers
|
|
261
|
+
handleStopDevServer,
|
|
262
|
+
handleRestartDevServer,
|
|
263
|
+
handleRemoveDevServer,
|
|
264
|
+
|
|
265
|
+
// Bulk operations
|
|
266
|
+
handleBulkDelete,
|
|
267
|
+
handleRefresh,
|
|
268
|
+
};
|
|
269
|
+
}
|