@siteboon/claude-code-ui 1.8.2 → 1.8.4
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/dist/assets/index-CeR_JfKq.js +895 -0
- package/dist/assets/index-Co7ALK3i.css +32 -0
- package/{index.html → dist/index.html} +2 -1
- package/package.json +6 -1
- package/server/database/auth.db +0 -0
- package/.env.example +0 -12
- package/.nvmrc +0 -1
- package/postcss.config.js +0 -6
- package/src/App.jsx +0 -751
- package/src/components/ChatInterface.jsx +0 -3485
- package/src/components/ClaudeLogo.jsx +0 -11
- package/src/components/ClaudeStatus.jsx +0 -107
- package/src/components/CodeEditor.jsx +0 -422
- package/src/components/CreateTaskModal.jsx +0 -88
- package/src/components/CursorLogo.jsx +0 -9
- package/src/components/DarkModeToggle.jsx +0 -35
- package/src/components/DiffViewer.jsx +0 -41
- package/src/components/ErrorBoundary.jsx +0 -73
- package/src/components/FileTree.jsx +0 -480
- package/src/components/GitPanel.jsx +0 -1283
- package/src/components/ImageViewer.jsx +0 -54
- package/src/components/LoginForm.jsx +0 -110
- package/src/components/MainContent.jsx +0 -577
- package/src/components/MicButton.jsx +0 -272
- package/src/components/MobileNav.jsx +0 -88
- package/src/components/NextTaskBanner.jsx +0 -695
- package/src/components/PRDEditor.jsx +0 -871
- package/src/components/ProtectedRoute.jsx +0 -44
- package/src/components/QuickSettingsPanel.jsx +0 -262
- package/src/components/Settings.jsx +0 -2023
- package/src/components/SetupForm.jsx +0 -135
- package/src/components/Shell.jsx +0 -663
- package/src/components/Sidebar.jsx +0 -1665
- package/src/components/StandaloneShell.jsx +0 -106
- package/src/components/TaskCard.jsx +0 -210
- package/src/components/TaskDetail.jsx +0 -406
- package/src/components/TaskIndicator.jsx +0 -108
- package/src/components/TaskList.jsx +0 -1054
- package/src/components/TaskMasterSetupWizard.jsx +0 -603
- package/src/components/TaskMasterStatus.jsx +0 -86
- package/src/components/TodoList.jsx +0 -91
- package/src/components/Tooltip.jsx +0 -91
- package/src/components/ui/badge.jsx +0 -31
- package/src/components/ui/button.jsx +0 -46
- package/src/components/ui/input.jsx +0 -19
- package/src/components/ui/scroll-area.jsx +0 -23
- package/src/contexts/AuthContext.jsx +0 -158
- package/src/contexts/TaskMasterContext.jsx +0 -324
- package/src/contexts/TasksSettingsContext.jsx +0 -95
- package/src/contexts/ThemeContext.jsx +0 -94
- package/src/contexts/WebSocketContext.jsx +0 -29
- package/src/hooks/useAudioRecorder.js +0 -109
- package/src/hooks/useVersionCheck.js +0 -39
- package/src/index.css +0 -822
- package/src/lib/utils.js +0 -6
- package/src/main.jsx +0 -10
- package/src/utils/api.js +0 -141
- package/src/utils/websocket.js +0 -109
- package/src/utils/whisper.js +0 -37
- package/tailwind.config.js +0 -63
- package/vite.config.js +0 -29
- /package/{public → dist}/convert-icons.md +0 -0
- /package/{public → dist}/favicon.png +0 -0
- /package/{public → dist}/favicon.svg +0 -0
- /package/{public → dist}/generate-icons.js +0 -0
- /package/{public → dist}/icons/claude-ai-icon.svg +0 -0
- /package/{public → dist}/icons/cursor.svg +0 -0
- /package/{public → dist}/icons/generate-icons.md +0 -0
- /package/{public → dist}/icons/icon-128x128.png +0 -0
- /package/{public → dist}/icons/icon-128x128.svg +0 -0
- /package/{public → dist}/icons/icon-144x144.png +0 -0
- /package/{public → dist}/icons/icon-144x144.svg +0 -0
- /package/{public → dist}/icons/icon-152x152.png +0 -0
- /package/{public → dist}/icons/icon-152x152.svg +0 -0
- /package/{public → dist}/icons/icon-192x192.png +0 -0
- /package/{public → dist}/icons/icon-192x192.svg +0 -0
- /package/{public → dist}/icons/icon-384x384.png +0 -0
- /package/{public → dist}/icons/icon-384x384.svg +0 -0
- /package/{public → dist}/icons/icon-512x512.png +0 -0
- /package/{public → dist}/icons/icon-512x512.svg +0 -0
- /package/{public → dist}/icons/icon-72x72.png +0 -0
- /package/{public → dist}/icons/icon-72x72.svg +0 -0
- /package/{public → dist}/icons/icon-96x96.png +0 -0
- /package/{public → dist}/icons/icon-96x96.svg +0 -0
- /package/{public → dist}/icons/icon-template.svg +0 -0
- /package/{public → dist}/logo.svg +0 -0
- /package/{public → dist}/manifest.json +0 -0
- /package/{public → dist}/screenshots/cli-selection.png +0 -0
- /package/{public → dist}/screenshots/desktop-main.png +0 -0
- /package/{public → dist}/screenshots/mobile-chat.png +0 -0
- /package/{public → dist}/screenshots/tools-modal.png +0 -0
- /package/{public → dist}/sw.js +0 -0
|
@@ -1,577 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* MainContent.jsx - Main Content Area with Session Protection Props Passthrough
|
|
3
|
-
*
|
|
4
|
-
* SESSION PROTECTION PASSTHROUGH:
|
|
5
|
-
* ===============================
|
|
6
|
-
*
|
|
7
|
-
* This component serves as a passthrough layer for Session Protection functions:
|
|
8
|
-
* - Receives session management functions from App.jsx
|
|
9
|
-
* - Passes them down to ChatInterface.jsx
|
|
10
|
-
*
|
|
11
|
-
* No session protection logic is implemented here - it's purely a props bridge.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import React, { useState, useEffect } from 'react';
|
|
15
|
-
import ChatInterface from './ChatInterface';
|
|
16
|
-
import FileTree from './FileTree';
|
|
17
|
-
import CodeEditor from './CodeEditor';
|
|
18
|
-
import StandaloneShell from './StandaloneShell';
|
|
19
|
-
import GitPanel from './GitPanel';
|
|
20
|
-
import ErrorBoundary from './ErrorBoundary';
|
|
21
|
-
import ClaudeLogo from './ClaudeLogo';
|
|
22
|
-
import CursorLogo from './CursorLogo';
|
|
23
|
-
import TaskList from './TaskList';
|
|
24
|
-
import TaskDetail from './TaskDetail';
|
|
25
|
-
import PRDEditor from './PRDEditor';
|
|
26
|
-
import Tooltip from './Tooltip';
|
|
27
|
-
import { useTaskMaster } from '../contexts/TaskMasterContext';
|
|
28
|
-
import { useTasksSettings } from '../contexts/TasksSettingsContext';
|
|
29
|
-
import { api } from '../utils/api';
|
|
30
|
-
|
|
31
|
-
function MainContent({
|
|
32
|
-
selectedProject,
|
|
33
|
-
selectedSession,
|
|
34
|
-
activeTab,
|
|
35
|
-
setActiveTab,
|
|
36
|
-
ws,
|
|
37
|
-
sendMessage,
|
|
38
|
-
messages,
|
|
39
|
-
isMobile,
|
|
40
|
-
isPWA,
|
|
41
|
-
onMenuClick,
|
|
42
|
-
isLoading,
|
|
43
|
-
onInputFocusChange,
|
|
44
|
-
// Session Protection Props: Functions passed down from App.jsx to manage active session state
|
|
45
|
-
// These functions control when project updates are paused during active conversations
|
|
46
|
-
onSessionActive, // Mark session as active when user sends message
|
|
47
|
-
onSessionInactive, // Mark session as inactive when conversation completes/aborts
|
|
48
|
-
onReplaceTemporarySession, // Replace temporary session ID with real session ID from WebSocket
|
|
49
|
-
onNavigateToSession, // Navigate to a specific session (for Claude CLI session duplication workaround)
|
|
50
|
-
onShowSettings, // Show tools settings panel
|
|
51
|
-
autoExpandTools, // Auto-expand tool accordions
|
|
52
|
-
showRawParameters, // Show raw parameters in tool accordions
|
|
53
|
-
autoScrollToBottom, // Auto-scroll to bottom when new messages arrive
|
|
54
|
-
sendByCtrlEnter // Send by Ctrl+Enter mode for East Asian language input
|
|
55
|
-
}) {
|
|
56
|
-
const [editingFile, setEditingFile] = useState(null);
|
|
57
|
-
const [selectedTask, setSelectedTask] = useState(null);
|
|
58
|
-
const [showTaskDetail, setShowTaskDetail] = useState(false);
|
|
59
|
-
|
|
60
|
-
// PRD Editor state
|
|
61
|
-
const [showPRDEditor, setShowPRDEditor] = useState(false);
|
|
62
|
-
const [selectedPRD, setSelectedPRD] = useState(null);
|
|
63
|
-
const [existingPRDs, setExistingPRDs] = useState([]);
|
|
64
|
-
const [prdNotification, setPRDNotification] = useState(null);
|
|
65
|
-
|
|
66
|
-
// TaskMaster context
|
|
67
|
-
const { tasks, currentProject, refreshTasks, setCurrentProject } = useTaskMaster();
|
|
68
|
-
const { tasksEnabled, isTaskMasterInstalled, isTaskMasterReady } = useTasksSettings();
|
|
69
|
-
|
|
70
|
-
// Only show tasks tab if TaskMaster is installed and enabled
|
|
71
|
-
const shouldShowTasksTab = tasksEnabled && isTaskMasterInstalled;
|
|
72
|
-
|
|
73
|
-
// Sync selectedProject with TaskMaster context
|
|
74
|
-
useEffect(() => {
|
|
75
|
-
if (selectedProject && selectedProject !== currentProject) {
|
|
76
|
-
setCurrentProject(selectedProject);
|
|
77
|
-
}
|
|
78
|
-
}, [selectedProject, currentProject, setCurrentProject]);
|
|
79
|
-
|
|
80
|
-
// Switch away from tasks tab when tasks are disabled or TaskMaster is not installed
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (!shouldShowTasksTab && activeTab === 'tasks') {
|
|
83
|
-
setActiveTab('chat');
|
|
84
|
-
}
|
|
85
|
-
}, [shouldShowTasksTab, activeTab, setActiveTab]);
|
|
86
|
-
|
|
87
|
-
// Load existing PRDs when current project changes
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
const loadExistingPRDs = async () => {
|
|
90
|
-
if (!currentProject?.name) {
|
|
91
|
-
setExistingPRDs([]);
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const response = await api.get(`/taskmaster/prd/${encodeURIComponent(currentProject.name)}`);
|
|
97
|
-
if (response.ok) {
|
|
98
|
-
const data = await response.json();
|
|
99
|
-
setExistingPRDs(data.prdFiles || []);
|
|
100
|
-
} else {
|
|
101
|
-
setExistingPRDs([]);
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error('Failed to load existing PRDs:', error);
|
|
105
|
-
setExistingPRDs([]);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
loadExistingPRDs();
|
|
110
|
-
}, [currentProject?.name]);
|
|
111
|
-
|
|
112
|
-
const handleFileOpen = (filePath, diffInfo = null) => {
|
|
113
|
-
// Create a file object that CodeEditor expects
|
|
114
|
-
const file = {
|
|
115
|
-
name: filePath.split('/').pop(),
|
|
116
|
-
path: filePath,
|
|
117
|
-
projectName: selectedProject?.name,
|
|
118
|
-
diffInfo: diffInfo // Pass along diff information if available
|
|
119
|
-
};
|
|
120
|
-
setEditingFile(file);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
const handleCloseEditor = () => {
|
|
124
|
-
setEditingFile(null);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const handleTaskClick = (task) => {
|
|
128
|
-
// If task is just an ID (from dependency click), find the full task object
|
|
129
|
-
if (typeof task === 'object' && task.id && !task.title) {
|
|
130
|
-
const fullTask = tasks?.find(t => t.id === task.id);
|
|
131
|
-
if (fullTask) {
|
|
132
|
-
setSelectedTask(fullTask);
|
|
133
|
-
setShowTaskDetail(true);
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
setSelectedTask(task);
|
|
137
|
-
setShowTaskDetail(true);
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const handleTaskDetailClose = () => {
|
|
142
|
-
setShowTaskDetail(false);
|
|
143
|
-
setSelectedTask(null);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const handleTaskStatusChange = (taskId, newStatus) => {
|
|
147
|
-
// This would integrate with TaskMaster API to update task status
|
|
148
|
-
console.log('Update task status:', taskId, newStatus);
|
|
149
|
-
refreshTasks?.();
|
|
150
|
-
};
|
|
151
|
-
if (isLoading) {
|
|
152
|
-
return (
|
|
153
|
-
<div className="h-full flex flex-col">
|
|
154
|
-
{/* Header with menu button for mobile */}
|
|
155
|
-
{isMobile && (
|
|
156
|
-
<div
|
|
157
|
-
className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-3 sm:p-4 pwa-header-safe flex-shrink-0"
|
|
158
|
-
>
|
|
159
|
-
<button
|
|
160
|
-
onClick={onMenuClick}
|
|
161
|
-
className="p-1.5 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
|
162
|
-
>
|
|
163
|
-
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
164
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
165
|
-
</svg>
|
|
166
|
-
</button>
|
|
167
|
-
</div>
|
|
168
|
-
)}
|
|
169
|
-
<div className="flex-1 flex items-center justify-center">
|
|
170
|
-
<div className="text-center text-gray-500 dark:text-gray-400">
|
|
171
|
-
<div className="w-12 h-12 mx-auto mb-4">
|
|
172
|
-
<div
|
|
173
|
-
className="w-full h-full rounded-full border-4 border-gray-200 border-t-blue-500"
|
|
174
|
-
style={{
|
|
175
|
-
animation: 'spin 1s linear infinite',
|
|
176
|
-
WebkitAnimation: 'spin 1s linear infinite',
|
|
177
|
-
MozAnimation: 'spin 1s linear infinite'
|
|
178
|
-
}}
|
|
179
|
-
/>
|
|
180
|
-
</div>
|
|
181
|
-
<h2 className="text-xl font-semibold mb-2">Loading Claude Code UI</h2>
|
|
182
|
-
<p>Setting up your workspace...</p>
|
|
183
|
-
</div>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (!selectedProject) {
|
|
190
|
-
return (
|
|
191
|
-
<div className="h-full flex flex-col">
|
|
192
|
-
{/* Header with menu button for mobile */}
|
|
193
|
-
{isMobile && (
|
|
194
|
-
<div
|
|
195
|
-
className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-3 sm:p-4 pwa-header-safe flex-shrink-0"
|
|
196
|
-
>
|
|
197
|
-
<button
|
|
198
|
-
onClick={onMenuClick}
|
|
199
|
-
className="p-1.5 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 pwa-menu-button"
|
|
200
|
-
>
|
|
201
|
-
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
202
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
203
|
-
</svg>
|
|
204
|
-
</button>
|
|
205
|
-
</div>
|
|
206
|
-
)}
|
|
207
|
-
<div className="flex-1 flex items-center justify-center">
|
|
208
|
-
<div className="text-center text-gray-500 dark:text-gray-400 max-w-md mx-auto px-6">
|
|
209
|
-
<div className="w-16 h-16 mx-auto mb-6 bg-gray-100 dark:bg-gray-800 rounded-full flex items-center justify-center">
|
|
210
|
-
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
211
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-5l-2-2H5a2 2 0 00-2 2z" />
|
|
212
|
-
</svg>
|
|
213
|
-
</div>
|
|
214
|
-
<h2 className="text-2xl font-semibold mb-3 text-gray-900 dark:text-white">Choose Your Project</h2>
|
|
215
|
-
<p className="text-gray-600 dark:text-gray-300 mb-6 leading-relaxed">
|
|
216
|
-
Select a project from the sidebar to start coding with Claude. Each project contains your chat sessions and file history.
|
|
217
|
-
</p>
|
|
218
|
-
<div className="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4 border border-blue-200 dark:border-blue-800">
|
|
219
|
-
<p className="text-sm text-blue-700 dark:text-blue-300">
|
|
220
|
-
💡 <strong>Tip:</strong> {isMobile ? 'Tap the menu button above to access projects' : 'Create a new project by clicking the folder icon in the sidebar'}
|
|
221
|
-
</p>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
</div>
|
|
225
|
-
</div>
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return (
|
|
230
|
-
<div className="h-full flex flex-col">
|
|
231
|
-
{/* Header with tabs */}
|
|
232
|
-
<div
|
|
233
|
-
className="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 p-3 sm:p-4 pwa-header-safe flex-shrink-0"
|
|
234
|
-
>
|
|
235
|
-
<div className="flex items-center justify-between">
|
|
236
|
-
<div className="flex items-center space-x-2 sm:space-x-3">
|
|
237
|
-
{isMobile && (
|
|
238
|
-
<button
|
|
239
|
-
onClick={onMenuClick}
|
|
240
|
-
onTouchStart={(e) => {
|
|
241
|
-
e.preventDefault();
|
|
242
|
-
onMenuClick();
|
|
243
|
-
}}
|
|
244
|
-
className="p-2.5 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white rounded-md hover:bg-gray-100 dark:hover:bg-gray-700 touch-manipulation active:scale-95 pwa-menu-button"
|
|
245
|
-
>
|
|
246
|
-
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
247
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
|
248
|
-
</svg>
|
|
249
|
-
</button>
|
|
250
|
-
)}
|
|
251
|
-
<div className="min-w-0 flex items-center gap-2">
|
|
252
|
-
{activeTab === 'chat' && selectedSession && (
|
|
253
|
-
<div className="w-6 h-6 flex-shrink-0 flex items-center justify-center">
|
|
254
|
-
{selectedSession.__provider === 'cursor' ? (
|
|
255
|
-
<CursorLogo className="w-5 h-5" />
|
|
256
|
-
) : (
|
|
257
|
-
<ClaudeLogo className="w-5 h-5" />
|
|
258
|
-
)}
|
|
259
|
-
</div>
|
|
260
|
-
)}
|
|
261
|
-
<div className="flex-1 min-w-0">
|
|
262
|
-
{activeTab === 'chat' && selectedSession ? (
|
|
263
|
-
<div>
|
|
264
|
-
<h2 className="text-base sm:text-lg font-semibold text-gray-900 dark:text-white truncate">
|
|
265
|
-
{selectedSession.__provider === 'cursor' ? (selectedSession.name || 'Untitled Session') : (selectedSession.summary || 'New Session')}
|
|
266
|
-
</h2>
|
|
267
|
-
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
|
268
|
-
{selectedProject.displayName} <span className="hidden sm:inline">• {selectedSession.id}</span>
|
|
269
|
-
</div>
|
|
270
|
-
</div>
|
|
271
|
-
) : activeTab === 'chat' && !selectedSession ? (
|
|
272
|
-
<div>
|
|
273
|
-
<h2 className="text-base sm:text-lg font-semibold text-gray-900 dark:text-white">
|
|
274
|
-
New Session
|
|
275
|
-
</h2>
|
|
276
|
-
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
|
277
|
-
{selectedProject.displayName}
|
|
278
|
-
</div>
|
|
279
|
-
</div>
|
|
280
|
-
) : (
|
|
281
|
-
<div>
|
|
282
|
-
<h2 className="text-base sm:text-lg font-semibold text-gray-900 dark:text-white">
|
|
283
|
-
{activeTab === 'files' ? 'Project Files' :
|
|
284
|
-
activeTab === 'git' ? 'Source Control' :
|
|
285
|
-
(activeTab === 'tasks' && shouldShowTasksTab) ? 'TaskMaster' :
|
|
286
|
-
'Project'}
|
|
287
|
-
</h2>
|
|
288
|
-
<div className="text-xs text-gray-500 dark:text-gray-400 truncate">
|
|
289
|
-
{selectedProject.displayName}
|
|
290
|
-
</div>
|
|
291
|
-
</div>
|
|
292
|
-
)}
|
|
293
|
-
</div>
|
|
294
|
-
</div>
|
|
295
|
-
</div>
|
|
296
|
-
|
|
297
|
-
{/* Modern Tab Navigation - Right Side */}
|
|
298
|
-
<div className="flex-shrink-0 hidden sm:block">
|
|
299
|
-
<div className="relative flex bg-gray-100 dark:bg-gray-800 rounded-lg p-1">
|
|
300
|
-
<Tooltip content="Chat" position="bottom">
|
|
301
|
-
<button
|
|
302
|
-
onClick={() => setActiveTab('chat')}
|
|
303
|
-
className={`relative px-2 sm:px-3 py-1.5 text-xs sm:text-sm font-medium rounded-md ${
|
|
304
|
-
activeTab === 'chat'
|
|
305
|
-
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
306
|
-
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-700'
|
|
307
|
-
}`}
|
|
308
|
-
>
|
|
309
|
-
<span className="flex items-center gap-1 sm:gap-1.5">
|
|
310
|
-
<svg className="w-3 sm:w-3.5 h-3 sm:h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
311
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
|
312
|
-
</svg>
|
|
313
|
-
<span className="hidden md:hidden lg:inline">Chat</span>
|
|
314
|
-
</span>
|
|
315
|
-
</button>
|
|
316
|
-
</Tooltip>
|
|
317
|
-
<Tooltip content="Shell" position="bottom">
|
|
318
|
-
<button
|
|
319
|
-
onClick={() => setActiveTab('shell')}
|
|
320
|
-
className={`relative px-2 sm:px-3 py-1.5 text-xs sm:text-sm font-medium rounded-md transition-all duration-200 ${
|
|
321
|
-
activeTab === 'shell'
|
|
322
|
-
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
323
|
-
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-700'
|
|
324
|
-
}`}
|
|
325
|
-
>
|
|
326
|
-
<span className="flex items-center gap-1 sm:gap-1.5">
|
|
327
|
-
<svg className="w-3 sm:w-3.5 h-3 sm:h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
328
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2z" />
|
|
329
|
-
</svg>
|
|
330
|
-
<span className="hidden md:hidden lg:inline">Shell</span>
|
|
331
|
-
</span>
|
|
332
|
-
</button>
|
|
333
|
-
</Tooltip>
|
|
334
|
-
<Tooltip content="Files" position="bottom">
|
|
335
|
-
<button
|
|
336
|
-
onClick={() => setActiveTab('files')}
|
|
337
|
-
className={`relative px-2 sm:px-3 py-1.5 text-xs sm:text-sm font-medium rounded-md transition-all duration-200 ${
|
|
338
|
-
activeTab === 'files'
|
|
339
|
-
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
340
|
-
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-700'
|
|
341
|
-
}`}
|
|
342
|
-
>
|
|
343
|
-
<span className="flex items-center gap-1 sm:gap-1.5">
|
|
344
|
-
<svg className="w-3 sm:w-3.5 h-3 sm:h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
345
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-5l-2-2H5a2 2 0 00-2 2z" />
|
|
346
|
-
</svg>
|
|
347
|
-
<span className="hidden md:hidden lg:inline">Files</span>
|
|
348
|
-
</span>
|
|
349
|
-
</button>
|
|
350
|
-
</Tooltip>
|
|
351
|
-
<Tooltip content="Source Control" position="bottom">
|
|
352
|
-
<button
|
|
353
|
-
onClick={() => setActiveTab('git')}
|
|
354
|
-
className={`relative px-2 sm:px-3 py-1.5 text-xs sm:text-sm font-medium rounded-md transition-all duration-200 ${
|
|
355
|
-
activeTab === 'git'
|
|
356
|
-
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
357
|
-
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-700'
|
|
358
|
-
}`}
|
|
359
|
-
>
|
|
360
|
-
<span className="flex items-center gap-1 sm:gap-1.5">
|
|
361
|
-
<svg className="w-3 sm:w-3.5 h-3 sm:h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
362
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
363
|
-
</svg>
|
|
364
|
-
<span className="hidden md:hidden lg:inline">Source Control</span>
|
|
365
|
-
</span>
|
|
366
|
-
</button>
|
|
367
|
-
</Tooltip>
|
|
368
|
-
{shouldShowTasksTab && (
|
|
369
|
-
<Tooltip content="Tasks" position="bottom">
|
|
370
|
-
<button
|
|
371
|
-
onClick={() => setActiveTab('tasks')}
|
|
372
|
-
className={`relative px-2 sm:px-3 py-1.5 text-xs sm:text-sm font-medium rounded-md transition-all duration-200 ${
|
|
373
|
-
activeTab === 'tasks'
|
|
374
|
-
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
375
|
-
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-700'
|
|
376
|
-
}`}
|
|
377
|
-
>
|
|
378
|
-
<span className="flex items-center gap-1 sm:gap-1.5">
|
|
379
|
-
<svg className="w-3 sm:w-3.5 h-3 sm:h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
380
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
|
|
381
|
-
</svg>
|
|
382
|
-
<span className="hidden md:hidden lg:inline">Tasks</span>
|
|
383
|
-
</span>
|
|
384
|
-
</button>
|
|
385
|
-
</Tooltip>
|
|
386
|
-
)}
|
|
387
|
-
{/* <button
|
|
388
|
-
onClick={() => setActiveTab('preview')}
|
|
389
|
-
className={`relative px-2 sm:px-3 py-1.5 text-xs sm:text-sm font-medium rounded-md transition-all duration-200 ${
|
|
390
|
-
activeTab === 'preview'
|
|
391
|
-
? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
|
|
392
|
-
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-200 dark:hover:bg-gray-700'
|
|
393
|
-
}`}
|
|
394
|
-
>
|
|
395
|
-
<span className="flex items-center gap-1 sm:gap-1.5">
|
|
396
|
-
<svg className="w-3 sm:w-3.5 h-3 sm:h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
397
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
|
398
|
-
</svg>
|
|
399
|
-
<span className="hidden sm:inline">Preview</span>
|
|
400
|
-
</span>
|
|
401
|
-
</button> */}
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
|
|
407
|
-
{/* Content Area */}
|
|
408
|
-
<div className="flex-1 flex flex-col min-h-0 overflow-hidden">
|
|
409
|
-
<div className={`h-full ${activeTab === 'chat' ? 'block' : 'hidden'}`}>
|
|
410
|
-
<ErrorBoundary showDetails={true}>
|
|
411
|
-
<ChatInterface
|
|
412
|
-
selectedProject={selectedProject}
|
|
413
|
-
selectedSession={selectedSession}
|
|
414
|
-
ws={ws}
|
|
415
|
-
sendMessage={sendMessage}
|
|
416
|
-
messages={messages}
|
|
417
|
-
onFileOpen={handleFileOpen}
|
|
418
|
-
onInputFocusChange={onInputFocusChange}
|
|
419
|
-
onSessionActive={onSessionActive}
|
|
420
|
-
onSessionInactive={onSessionInactive}
|
|
421
|
-
onReplaceTemporarySession={onReplaceTemporarySession}
|
|
422
|
-
onNavigateToSession={onNavigateToSession}
|
|
423
|
-
onShowSettings={onShowSettings}
|
|
424
|
-
autoExpandTools={autoExpandTools}
|
|
425
|
-
showRawParameters={showRawParameters}
|
|
426
|
-
autoScrollToBottom={autoScrollToBottom}
|
|
427
|
-
sendByCtrlEnter={sendByCtrlEnter}
|
|
428
|
-
onShowAllTasks={tasksEnabled ? () => setActiveTab('tasks') : null}
|
|
429
|
-
/>
|
|
430
|
-
</ErrorBoundary>
|
|
431
|
-
</div>
|
|
432
|
-
<div className={`h-full overflow-hidden ${activeTab === 'files' ? 'block' : 'hidden'}`}>
|
|
433
|
-
<FileTree selectedProject={selectedProject} />
|
|
434
|
-
</div>
|
|
435
|
-
<div className={`h-full overflow-hidden ${activeTab === 'shell' ? 'block' : 'hidden'}`}>
|
|
436
|
-
<StandaloneShell
|
|
437
|
-
project={selectedProject}
|
|
438
|
-
session={selectedSession}
|
|
439
|
-
isActive={activeTab === 'shell'}
|
|
440
|
-
showHeader={false}
|
|
441
|
-
/>
|
|
442
|
-
</div>
|
|
443
|
-
<div className={`h-full overflow-hidden ${activeTab === 'git' ? 'block' : 'hidden'}`}>
|
|
444
|
-
<GitPanel selectedProject={selectedProject} isMobile={isMobile} />
|
|
445
|
-
</div>
|
|
446
|
-
{shouldShowTasksTab && (
|
|
447
|
-
<div className={`h-full ${activeTab === 'tasks' ? 'block' : 'hidden'}`}>
|
|
448
|
-
<div className="h-full flex flex-col overflow-hidden">
|
|
449
|
-
<TaskList
|
|
450
|
-
tasks={tasks || []}
|
|
451
|
-
onTaskClick={handleTaskClick}
|
|
452
|
-
showParentTasks={true}
|
|
453
|
-
className="flex-1 overflow-y-auto p-4"
|
|
454
|
-
currentProject={currentProject}
|
|
455
|
-
onTaskCreated={refreshTasks}
|
|
456
|
-
onShowPRDEditor={(prd = null) => {
|
|
457
|
-
setSelectedPRD(prd);
|
|
458
|
-
setShowPRDEditor(true);
|
|
459
|
-
}}
|
|
460
|
-
existingPRDs={existingPRDs}
|
|
461
|
-
onRefreshPRDs={(showNotification = false) => {
|
|
462
|
-
// Reload existing PRDs
|
|
463
|
-
if (currentProject?.name) {
|
|
464
|
-
api.get(`/taskmaster/prd/${encodeURIComponent(currentProject.name)}`)
|
|
465
|
-
.then(response => response.ok ? response.json() : Promise.reject())
|
|
466
|
-
.then(data => {
|
|
467
|
-
setExistingPRDs(data.prdFiles || []);
|
|
468
|
-
if (showNotification) {
|
|
469
|
-
setPRDNotification('PRD saved successfully!');
|
|
470
|
-
setTimeout(() => setPRDNotification(null), 3000);
|
|
471
|
-
}
|
|
472
|
-
})
|
|
473
|
-
.catch(error => console.error('Failed to refresh PRDs:', error));
|
|
474
|
-
}
|
|
475
|
-
}}
|
|
476
|
-
/>
|
|
477
|
-
</div>
|
|
478
|
-
</div>
|
|
479
|
-
)}
|
|
480
|
-
<div className={`h-full overflow-hidden ${activeTab === 'preview' ? 'block' : 'hidden'}`}>
|
|
481
|
-
{/* <LivePreviewPanel
|
|
482
|
-
selectedProject={selectedProject}
|
|
483
|
-
serverStatus={serverStatus}
|
|
484
|
-
serverUrl={serverUrl}
|
|
485
|
-
availableScripts={availableScripts}
|
|
486
|
-
onStartServer={(script) => {
|
|
487
|
-
sendMessage({
|
|
488
|
-
type: 'server:start',
|
|
489
|
-
projectPath: selectedProject?.fullPath,
|
|
490
|
-
script: script
|
|
491
|
-
});
|
|
492
|
-
}}
|
|
493
|
-
onStopServer={() => {
|
|
494
|
-
sendMessage({
|
|
495
|
-
type: 'server:stop',
|
|
496
|
-
projectPath: selectedProject?.fullPath
|
|
497
|
-
});
|
|
498
|
-
}}
|
|
499
|
-
onScriptSelect={setCurrentScript}
|
|
500
|
-
currentScript={currentScript}
|
|
501
|
-
isMobile={isMobile}
|
|
502
|
-
serverLogs={serverLogs}
|
|
503
|
-
onClearLogs={() => setServerLogs([])}
|
|
504
|
-
/> */}
|
|
505
|
-
</div>
|
|
506
|
-
</div>
|
|
507
|
-
|
|
508
|
-
{/* Code Editor Modal */}
|
|
509
|
-
{editingFile && (
|
|
510
|
-
<CodeEditor
|
|
511
|
-
file={editingFile}
|
|
512
|
-
onClose={handleCloseEditor}
|
|
513
|
-
projectPath={selectedProject?.path}
|
|
514
|
-
/>
|
|
515
|
-
)}
|
|
516
|
-
|
|
517
|
-
{/* Task Detail Modal */}
|
|
518
|
-
{shouldShowTasksTab && showTaskDetail && selectedTask && (
|
|
519
|
-
<TaskDetail
|
|
520
|
-
task={selectedTask}
|
|
521
|
-
isOpen={showTaskDetail}
|
|
522
|
-
onClose={handleTaskDetailClose}
|
|
523
|
-
onStatusChange={handleTaskStatusChange}
|
|
524
|
-
onTaskClick={handleTaskClick}
|
|
525
|
-
/>
|
|
526
|
-
)}
|
|
527
|
-
{/* PRD Editor Modal */}
|
|
528
|
-
{showPRDEditor && (
|
|
529
|
-
<PRDEditor
|
|
530
|
-
project={currentProject}
|
|
531
|
-
projectPath={currentProject?.fullPath || currentProject?.path}
|
|
532
|
-
onClose={() => {
|
|
533
|
-
setShowPRDEditor(false);
|
|
534
|
-
setSelectedPRD(null);
|
|
535
|
-
}}
|
|
536
|
-
isNewFile={!selectedPRD?.isExisting}
|
|
537
|
-
file={{
|
|
538
|
-
name: selectedPRD?.name || 'prd.txt',
|
|
539
|
-
content: selectedPRD?.content || ''
|
|
540
|
-
}}
|
|
541
|
-
onSave={async () => {
|
|
542
|
-
setShowPRDEditor(false);
|
|
543
|
-
setSelectedPRD(null);
|
|
544
|
-
|
|
545
|
-
// Reload existing PRDs with notification
|
|
546
|
-
try {
|
|
547
|
-
const response = await api.get(`/taskmaster/prd/${encodeURIComponent(currentProject.name)}`);
|
|
548
|
-
if (response.ok) {
|
|
549
|
-
const data = await response.json();
|
|
550
|
-
setExistingPRDs(data.prdFiles || []);
|
|
551
|
-
setPRDNotification('PRD saved successfully!');
|
|
552
|
-
setTimeout(() => setPRDNotification(null), 3000);
|
|
553
|
-
}
|
|
554
|
-
} catch (error) {
|
|
555
|
-
console.error('Failed to refresh PRDs:', error);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
refreshTasks?.();
|
|
559
|
-
}}
|
|
560
|
-
/>
|
|
561
|
-
)}
|
|
562
|
-
{/* PRD Notification */}
|
|
563
|
-
{prdNotification && (
|
|
564
|
-
<div className="fixed bottom-4 right-4 z-50 animate-in slide-in-from-bottom-2 duration-300">
|
|
565
|
-
<div className="bg-green-600 text-white px-4 py-3 rounded-lg shadow-lg flex items-center gap-3">
|
|
566
|
-
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
567
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
|
568
|
-
</svg>
|
|
569
|
-
<span className="font-medium">{prdNotification}</span>
|
|
570
|
-
</div>
|
|
571
|
-
</div>
|
|
572
|
-
)}
|
|
573
|
-
</div>
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
export default React.memo(MainContent);
|