@siteboon/claude-code-ui 1.8.2 → 1.8.3

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 (92) hide show
  1. package/dist/assets/index-BNGSzSdr.css +32 -0
  2. package/dist/assets/index-BZctMHnE.js +900 -0
  3. package/{index.html → dist/index.html} +4 -3
  4. package/package.json +6 -1
  5. package/server/database/auth.db +0 -0
  6. package/.env.example +0 -12
  7. package/.nvmrc +0 -1
  8. package/postcss.config.js +0 -6
  9. package/src/App.jsx +0 -751
  10. package/src/components/ChatInterface.jsx +0 -3485
  11. package/src/components/ClaudeLogo.jsx +0 -11
  12. package/src/components/ClaudeStatus.jsx +0 -107
  13. package/src/components/CodeEditor.jsx +0 -422
  14. package/src/components/CreateTaskModal.jsx +0 -88
  15. package/src/components/CursorLogo.jsx +0 -9
  16. package/src/components/DarkModeToggle.jsx +0 -35
  17. package/src/components/DiffViewer.jsx +0 -41
  18. package/src/components/ErrorBoundary.jsx +0 -73
  19. package/src/components/FileTree.jsx +0 -480
  20. package/src/components/GitPanel.jsx +0 -1283
  21. package/src/components/ImageViewer.jsx +0 -54
  22. package/src/components/LoginForm.jsx +0 -110
  23. package/src/components/MainContent.jsx +0 -577
  24. package/src/components/MicButton.jsx +0 -272
  25. package/src/components/MobileNav.jsx +0 -88
  26. package/src/components/NextTaskBanner.jsx +0 -695
  27. package/src/components/PRDEditor.jsx +0 -871
  28. package/src/components/ProtectedRoute.jsx +0 -44
  29. package/src/components/QuickSettingsPanel.jsx +0 -262
  30. package/src/components/Settings.jsx +0 -2023
  31. package/src/components/SetupForm.jsx +0 -135
  32. package/src/components/Shell.jsx +0 -663
  33. package/src/components/Sidebar.jsx +0 -1665
  34. package/src/components/StandaloneShell.jsx +0 -106
  35. package/src/components/TaskCard.jsx +0 -210
  36. package/src/components/TaskDetail.jsx +0 -406
  37. package/src/components/TaskIndicator.jsx +0 -108
  38. package/src/components/TaskList.jsx +0 -1054
  39. package/src/components/TaskMasterSetupWizard.jsx +0 -603
  40. package/src/components/TaskMasterStatus.jsx +0 -86
  41. package/src/components/TodoList.jsx +0 -91
  42. package/src/components/Tooltip.jsx +0 -91
  43. package/src/components/ui/badge.jsx +0 -31
  44. package/src/components/ui/button.jsx +0 -46
  45. package/src/components/ui/input.jsx +0 -19
  46. package/src/components/ui/scroll-area.jsx +0 -23
  47. package/src/contexts/AuthContext.jsx +0 -158
  48. package/src/contexts/TaskMasterContext.jsx +0 -324
  49. package/src/contexts/TasksSettingsContext.jsx +0 -95
  50. package/src/contexts/ThemeContext.jsx +0 -94
  51. package/src/contexts/WebSocketContext.jsx +0 -29
  52. package/src/hooks/useAudioRecorder.js +0 -109
  53. package/src/hooks/useVersionCheck.js +0 -39
  54. package/src/index.css +0 -822
  55. package/src/lib/utils.js +0 -6
  56. package/src/main.jsx +0 -10
  57. package/src/utils/api.js +0 -141
  58. package/src/utils/websocket.js +0 -109
  59. package/src/utils/whisper.js +0 -37
  60. package/tailwind.config.js +0 -63
  61. package/vite.config.js +0 -29
  62. /package/{public → dist}/convert-icons.md +0 -0
  63. /package/{public → dist}/favicon.png +0 -0
  64. /package/{public → dist}/favicon.svg +0 -0
  65. /package/{public → dist}/generate-icons.js +0 -0
  66. /package/{public → dist}/icons/claude-ai-icon.svg +0 -0
  67. /package/{public → dist}/icons/cursor.svg +0 -0
  68. /package/{public → dist}/icons/generate-icons.md +0 -0
  69. /package/{public → dist}/icons/icon-128x128.png +0 -0
  70. /package/{public → dist}/icons/icon-128x128.svg +0 -0
  71. /package/{public → dist}/icons/icon-144x144.png +0 -0
  72. /package/{public → dist}/icons/icon-144x144.svg +0 -0
  73. /package/{public → dist}/icons/icon-152x152.png +0 -0
  74. /package/{public → dist}/icons/icon-152x152.svg +0 -0
  75. /package/{public → dist}/icons/icon-192x192.png +0 -0
  76. /package/{public → dist}/icons/icon-192x192.svg +0 -0
  77. /package/{public → dist}/icons/icon-384x384.png +0 -0
  78. /package/{public → dist}/icons/icon-384x384.svg +0 -0
  79. /package/{public → dist}/icons/icon-512x512.png +0 -0
  80. /package/{public → dist}/icons/icon-512x512.svg +0 -0
  81. /package/{public → dist}/icons/icon-72x72.png +0 -0
  82. /package/{public → dist}/icons/icon-72x72.svg +0 -0
  83. /package/{public → dist}/icons/icon-96x96.png +0 -0
  84. /package/{public → dist}/icons/icon-96x96.svg +0 -0
  85. /package/{public → dist}/icons/icon-template.svg +0 -0
  86. /package/{public → dist}/logo.svg +0 -0
  87. /package/{public → dist}/manifest.json +0 -0
  88. /package/{public → dist}/screenshots/cli-selection.png +0 -0
  89. /package/{public → dist}/screenshots/desktop-main.png +0 -0
  90. /package/{public → dist}/screenshots/mobile-chat.png +0 -0
  91. /package/{public → dist}/screenshots/tools-modal.png +0 -0
  92. /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);