@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,1054 +0,0 @@
1
- import React, { useState, useMemo, useEffect } from 'react';
2
- import { Search, Filter, ArrowUpDown, ArrowUp, ArrowDown, List, Grid, ChevronDown, Columns, Plus, Settings, Terminal, FileText, HelpCircle, X } from 'lucide-react';
3
- import { cn } from '../lib/utils';
4
- import TaskCard from './TaskCard';
5
- import CreateTaskModal from './CreateTaskModal';
6
- import { useTaskMaster } from '../contexts/TaskMasterContext';
7
- import Shell from './Shell';
8
- import { api } from '../utils/api';
9
-
10
- const TaskList = ({
11
- tasks = [],
12
- onTaskClick,
13
- className = '',
14
- showParentTasks = false,
15
- defaultView = 'kanban', // 'list', 'grid', or 'kanban'
16
- currentProject,
17
- onTaskCreated,
18
- onShowPRDEditor,
19
- existingPRDs = [],
20
- onRefreshPRDs
21
- }) => {
22
- const [searchTerm, setSearchTerm] = useState('');
23
- const [statusFilter, setStatusFilter] = useState('all');
24
- const [priorityFilter, setPriorityFilter] = useState('all');
25
- const [sortBy, setSortBy] = useState('id'); // 'id', 'title', 'status', 'priority', 'updated'
26
- const [sortOrder, setSortOrder] = useState('asc'); // 'asc' or 'desc'
27
- const [viewMode, setViewMode] = useState(defaultView);
28
- const [showFilters, setShowFilters] = useState(false);
29
- const [showCreateModal, setShowCreateModal] = useState(false);
30
- const [showCLI, setShowCLI] = useState(false);
31
- const [showHelpGuide, setShowHelpGuide] = useState(false);
32
- const [isTaskMasterComplete, setIsTaskMasterComplete] = useState(false);
33
- const [showPRDDropdown, setShowPRDDropdown] = useState(false);
34
-
35
- const { projectTaskMaster, refreshProjects, refreshTasks, setCurrentProject } = useTaskMaster();
36
-
37
- // Close PRD dropdown when clicking outside
38
- useEffect(() => {
39
- const handleClickOutside = (event) => {
40
- if (showPRDDropdown && !event.target.closest('.relative')) {
41
- setShowPRDDropdown(false);
42
- }
43
- };
44
-
45
- document.addEventListener('mousedown', handleClickOutside);
46
- return () => document.removeEventListener('mousedown', handleClickOutside);
47
- }, [showPRDDropdown]);
48
-
49
- // Get unique status values from tasks
50
- const statuses = useMemo(() => {
51
- const statusSet = new Set(tasks.map(task => task.status).filter(Boolean));
52
- return Array.from(statusSet).sort();
53
- }, [tasks]);
54
-
55
- // Get unique priority values from tasks
56
- const priorities = useMemo(() => {
57
- const prioritySet = new Set(tasks.map(task => task.priority).filter(Boolean));
58
- return Array.from(prioritySet).sort();
59
- }, [tasks]);
60
-
61
- // Filter and sort tasks
62
- const filteredAndSortedTasks = useMemo(() => {
63
- let filtered = tasks.filter(task => {
64
- // Text search
65
- const searchLower = searchTerm.toLowerCase();
66
- const matchesSearch = !searchTerm ||
67
- task.title.toLowerCase().includes(searchLower) ||
68
- task.description?.toLowerCase().includes(searchLower) ||
69
- task.id.toString().includes(searchLower);
70
-
71
- // Status filter
72
- const matchesStatus = statusFilter === 'all' || task.status === statusFilter;
73
-
74
- // Priority filter
75
- const matchesPriority = priorityFilter === 'all' || task.priority === priorityFilter;
76
-
77
- return matchesSearch && matchesStatus && matchesPriority;
78
- });
79
-
80
- // Sort tasks
81
- filtered.sort((a, b) => {
82
- let aVal, bVal;
83
-
84
- switch (sortBy) {
85
- case 'title':
86
- aVal = a.title.toLowerCase();
87
- bVal = b.title.toLowerCase();
88
- break;
89
- case 'status':
90
- // Custom status ordering: pending, in-progress, done, blocked, deferred, cancelled
91
- const statusOrder = { pending: 1, 'in-progress': 2, done: 3, blocked: 4, deferred: 5, cancelled: 6 };
92
- aVal = statusOrder[a.status] || 99;
93
- bVal = statusOrder[b.status] || 99;
94
- break;
95
- case 'priority':
96
- // Custom priority ordering: high should be sorted first in descending
97
- const priorityOrder = { high: 3, medium: 2, low: 1 };
98
- aVal = priorityOrder[a.priority] || 0;
99
- bVal = priorityOrder[b.priority] || 0;
100
- break;
101
- case 'updated':
102
- aVal = new Date(a.updatedAt || a.createdAt || 0);
103
- bVal = new Date(b.updatedAt || b.createdAt || 0);
104
- break;
105
- case 'id':
106
- default:
107
- // Handle numeric and dotted IDs (1, 1.1, 1.2, 2, 2.1, etc.)
108
- const parseId = (id) => {
109
- const parts = id.toString().split('.');
110
- return parts.map(part => parseInt(part, 10));
111
- };
112
-
113
- const aIds = parseId(a.id);
114
- const bIds = parseId(b.id);
115
-
116
- // Compare each part
117
- for (let i = 0; i < Math.max(aIds.length, bIds.length); i++) {
118
- const aId = aIds[i] || 0;
119
- const bId = bIds[i] || 0;
120
- if (aId !== bId) {
121
- aVal = aId;
122
- bVal = bId;
123
- break;
124
- }
125
- }
126
- break;
127
- }
128
-
129
- if (sortBy === 'updated') {
130
- return sortOrder === 'asc' ? aVal - bVal : bVal - aVal;
131
- }
132
-
133
- if (typeof aVal === 'string') {
134
- return sortOrder === 'asc' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
135
- }
136
-
137
- return sortOrder === 'asc' ? aVal - bVal : bVal - aVal;
138
- });
139
-
140
- return filtered;
141
- }, [tasks, searchTerm, statusFilter, priorityFilter, sortBy, sortOrder]);
142
-
143
- // Organize tasks by status for Kanban view
144
- const kanbanColumns = useMemo(() => {
145
- const allColumns = [
146
- {
147
- id: 'pending',
148
- title: '📋 To Do',
149
- status: 'pending',
150
- color: 'bg-slate-50 dark:bg-slate-900/50 border-slate-200 dark:border-slate-700',
151
- headerColor: 'bg-slate-100 dark:bg-slate-800 text-slate-800 dark:text-slate-200'
152
- },
153
- {
154
- id: 'in-progress',
155
- title: '🚀 In Progress',
156
- status: 'in-progress',
157
- color: 'bg-blue-50 dark:bg-blue-900/50 border-blue-200 dark:border-blue-700',
158
- headerColor: 'bg-blue-100 dark:bg-blue-800 text-blue-800 dark:text-blue-200'
159
- },
160
- {
161
- id: 'done',
162
- title: '✅ Done',
163
- status: 'done',
164
- color: 'bg-emerald-50 dark:bg-emerald-900/50 border-emerald-200 dark:border-emerald-700',
165
- headerColor: 'bg-emerald-100 dark:bg-emerald-800 text-emerald-800 dark:text-emerald-200'
166
- },
167
- {
168
- id: 'blocked',
169
- title: '🚫 Blocked',
170
- status: 'blocked',
171
- color: 'bg-red-50 dark:bg-red-900/50 border-red-200 dark:border-red-700',
172
- headerColor: 'bg-red-100 dark:bg-red-800 text-red-800 dark:text-red-200'
173
- },
174
- {
175
- id: 'deferred',
176
- title: '⏳ Deferred',
177
- status: 'deferred',
178
- color: 'bg-amber-50 dark:bg-amber-900/50 border-amber-200 dark:border-amber-700',
179
- headerColor: 'bg-amber-100 dark:bg-amber-800 text-amber-800 dark:text-amber-200'
180
- },
181
- {
182
- id: 'cancelled',
183
- title: '❌ Cancelled',
184
- status: 'cancelled',
185
- color: 'bg-gray-50 dark:bg-gray-900/50 border-gray-200 dark:border-gray-700',
186
- headerColor: 'bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200'
187
- }
188
- ];
189
-
190
- // Only show columns that have tasks or are part of the main workflow
191
- const mainWorkflowStatuses = ['pending', 'in-progress', 'done'];
192
- const columnsWithTasks = allColumns.filter(column => {
193
- const hasTask = filteredAndSortedTasks.some(task => task.status === column.status);
194
- const isMainWorkflow = mainWorkflowStatuses.includes(column.status);
195
- return hasTask || isMainWorkflow;
196
- });
197
-
198
- return columnsWithTasks.map(column => ({
199
- ...column,
200
- tasks: filteredAndSortedTasks.filter(task => task.status === column.status)
201
- }));
202
- }, [filteredAndSortedTasks]);
203
-
204
- const handleSortChange = (newSortBy) => {
205
- if (sortBy === newSortBy) {
206
- setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
207
- } else {
208
- setSortBy(newSortBy);
209
- setSortOrder('asc');
210
- }
211
- };
212
-
213
- const clearFilters = () => {
214
- setSearchTerm('');
215
- setStatusFilter('all');
216
- setPriorityFilter('all');
217
- };
218
-
219
- const getSortIcon = (field) => {
220
- if (sortBy !== field) return <ArrowUpDown className="w-4 h-4" />;
221
- return sortOrder === 'asc' ? <ArrowUp className="w-4 h-4" /> : <ArrowDown className="w-4 h-4" />;
222
- };
223
-
224
- if (tasks.length === 0) {
225
- // Check if TaskMaster is configured by looking for .taskmaster directory
226
- const hasTaskMasterDirectory = currentProject?.taskMasterConfigured ||
227
- currentProject?.taskmaster?.hasTaskmaster ||
228
- projectTaskMaster?.hasTaskmaster;
229
-
230
- return (
231
- <div className={cn('text-center py-12', className)}>
232
- {!hasTaskMasterDirectory ? (
233
- // TaskMaster not configured
234
- <div className="max-w-md mx-auto">
235
- <div className="text-blue-600 dark:text-blue-400 mb-4">
236
- <Settings className="w-12 h-12 mx-auto mb-4" />
237
- </div>
238
- <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
239
- TaskMaster AI is not configured
240
- </h3>
241
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-6">
242
- TaskMaster helps break down complex projects into manageable tasks with AI-powered assistance
243
- </p>
244
-
245
- {/* What is TaskMaster section */}
246
- <div className="mb-6 p-4 bg-blue-50 dark:bg-blue-950 rounded-lg text-left">
247
- <h4 className="text-sm font-medium text-blue-900 dark:text-blue-100 mb-3">
248
- 🎯 What is TaskMaster?
249
- </h4>
250
- <div className="text-xs text-blue-800 dark:text-blue-200 space-y-1">
251
- <p>• <strong>AI-Powered Task Management:</strong> Break complex projects into manageable subtasks</p>
252
- <p>• <strong>PRD Templates:</strong> Generate tasks from Product Requirements Documents</p>
253
- <p>• <strong>Dependency Tracking:</strong> Understand task relationships and execution order</p>
254
- <p>• <strong>Progress Visualization:</strong> Kanban boards and detailed task analytics</p>
255
- <p>• <strong>CLI Integration:</strong> Use taskmaster commands for advanced workflows</p>
256
- </div>
257
- </div>
258
-
259
- <button
260
- onClick={() => {
261
- setIsTaskMasterComplete(false); // Reset completion state
262
- setShowCLI(true);
263
- }}
264
- className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-colors flex items-center gap-2 mx-auto"
265
- >
266
- <Terminal className="w-4 h-4" />
267
- Initialize TaskMaster AI
268
- </button>
269
- </div>
270
- ) : (
271
- // TaskMaster configured but no tasks - show Getting Started guide
272
- <div className="max-w-4xl mx-auto">
273
- <div className="bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950/50 dark:to-indigo-950/50 rounded-xl border border-blue-200 dark:border-blue-800 p-6 mb-6">
274
- <div className="flex items-center gap-3 mb-4">
275
- <div className="w-10 h-10 bg-blue-100 dark:bg-blue-900/50 rounded-lg flex items-center justify-center">
276
- <FileText className="w-5 h-5 text-blue-600 dark:text-blue-400" />
277
- </div>
278
- <div>
279
- <h2 className="text-xl font-semibold text-gray-900 dark:text-white">Getting Started with TaskMaster</h2>
280
- <p className="text-sm text-gray-600 dark:text-gray-400">TaskMaster is initialized! Here's what to do next:</p>
281
- </div>
282
- </div>
283
-
284
- <div className="space-y-4 text-left">
285
- <div className="grid gap-3">
286
- {/* Step 1 */}
287
- <div className="flex gap-3 p-3 bg-white dark:bg-gray-800/50 rounded-lg border border-blue-100 dark:border-blue-800/50">
288
- <div className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white text-xs font-semibold rounded-full flex items-center justify-center">1</div>
289
- <div>
290
- <h4 className="font-medium text-gray-900 dark:text-white mb-1">Create a Product Requirements Document (PRD)</h4>
291
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-2">Discuss your project idea and create a PRD that describes what you want to build.</p>
292
- <button
293
- onClick={() => {
294
- onShowPRDEditor?.();
295
- }}
296
- className="inline-flex items-center gap-1 text-xs bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 px-2 py-1 rounded hover:bg-purple-200 dark:hover:bg-purple-900/50 transition-colors"
297
- >
298
- <FileText className="w-3 h-3" />
299
- Add PRD
300
- </button>
301
-
302
- {/* Show existing PRDs if any */}
303
- {existingPRDs.length > 0 && (
304
- <div className="mt-3 pt-3 border-t border-gray-200 dark:border-gray-700">
305
- <p className="text-xs text-gray-500 dark:text-gray-400 mb-2">Existing PRDs:</p>
306
- <div className="flex flex-wrap gap-2">
307
- {existingPRDs.map((prd) => (
308
- <button
309
- key={prd.name}
310
- onClick={async () => {
311
- try {
312
- // Load the PRD content from the API
313
- const response = await api.get(`/taskmaster/prd/${encodeURIComponent(currentProject.name)}/${encodeURIComponent(prd.name)}`);
314
- if (response.ok) {
315
- const prdData = await response.json();
316
- onShowPRDEditor?.({
317
- name: prd.name,
318
- content: prdData.content,
319
- isExisting: true
320
- });
321
- } else {
322
- console.error('Failed to load PRD:', response.statusText);
323
- }
324
- } catch (error) {
325
- console.error('Error loading PRD:', error);
326
- }
327
- }}
328
- className="inline-flex items-center gap-1 text-xs bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-2 py-1 rounded hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
329
- >
330
- <FileText className="w-3 h-3" />
331
- {prd.name}
332
- </button>
333
- ))}
334
- </div>
335
- </div>
336
- )}
337
- </div>
338
- </div>
339
-
340
- {/* Step 2 */}
341
- <div className="flex gap-3 p-3 bg-white dark:bg-gray-800/50 rounded-lg border border-blue-100 dark:border-blue-800/50">
342
- <div className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white text-xs font-semibold rounded-full flex items-center justify-center">2</div>
343
- <div>
344
- <h4 className="font-medium text-gray-900 dark:text-white mb-1">Generate Tasks from PRD</h4>
345
- <p className="text-sm text-gray-600 dark:text-gray-400">Once you have a PRD, ask your AI assistant to parse it and TaskMaster will automatically break it down into manageable tasks with implementation details.</p>
346
- </div>
347
- </div>
348
-
349
- {/* Step 3 */}
350
- <div className="flex gap-3 p-3 bg-white dark:bg-gray-800/50 rounded-lg border border-blue-100 dark:border-blue-800/50">
351
- <div className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white text-xs font-semibold rounded-full flex items-center justify-center">3</div>
352
- <div>
353
- <h4 className="font-medium text-gray-900 dark:text-white mb-1">Analyze & Expand Tasks</h4>
354
- <p className="text-sm text-gray-600 dark:text-gray-400">Ask your AI assistant to analyze task complexity and expand them into detailed subtasks for easier implementation.</p>
355
- </div>
356
- </div>
357
-
358
- {/* Step 4 */}
359
- <div className="flex gap-3 p-3 bg-white dark:bg-gray-800/50 rounded-lg border border-blue-100 dark:border-blue-800/50">
360
- <div className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white text-xs font-semibold rounded-full flex items-center justify-center">4</div>
361
- <div>
362
- <h4 className="font-medium text-gray-900 dark:text-white mb-1">Start Building</h4>
363
- <p className="text-sm text-gray-600 dark:text-gray-400">Ask your AI assistant to begin working on tasks, update their status, and add new tasks as your project evolves.</p>
364
- </div>
365
- </div>
366
- </div>
367
-
368
- <div className="flex gap-3 pt-4 border-t border-blue-200 dark:border-blue-700">
369
- <button
370
- onClick={(e) => {
371
- e.preventDefault();
372
- e.stopPropagation();
373
- onShowPRDEditor?.();
374
- }}
375
- className="flex items-center gap-2 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg font-medium transition-colors cursor-pointer"
376
- style={{ zIndex: 10 }}
377
- >
378
- <FileText className="w-4 h-4" />
379
- Add PRD
380
- </button>
381
- </div>
382
- </div>
383
- </div>
384
-
385
- <div className="text-center">
386
- <div className="text-sm text-gray-500 dark:text-gray-400 mb-2">
387
- 💡 <strong>Tip:</strong> Start with a PRD to get the most out of TaskMaster's AI-powered task generation
388
- </div>
389
- </div>
390
- </div>
391
- )}
392
-
393
- {/* TaskMaster CLI Setup Modal */}
394
- {showCLI && (
395
- <div className="fixed inset-0 z-50 flex items-start justify-center p-4 pt-16 bg-black/50 backdrop-blur-sm">
396
- <div className="bg-white dark:bg-gray-900 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 w-full max-w-4xl h-[600px] flex flex-col">
397
- {/* Modal Header */}
398
- <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
399
- <div className="flex items-center gap-3">
400
- <div className="w-8 h-8 bg-blue-100 dark:bg-blue-900/50 rounded-lg flex items-center justify-center">
401
- <Terminal className="w-4 h-4 text-blue-600 dark:text-blue-400" />
402
- </div>
403
- <div>
404
- <h2 className="text-lg font-semibold text-gray-900 dark:text-white">TaskMaster Setup</h2>
405
- <p className="text-sm text-gray-500 dark:text-gray-400">Interactive CLI for {currentProject?.displayName}</p>
406
- </div>
407
- </div>
408
- <button
409
- onClick={() => {
410
- setShowCLI(false);
411
- // Refresh project data after closing CLI to detect TaskMaster initialization
412
- setTimeout(() => {
413
- refreshProjects();
414
- // Also refresh the current project's TaskMaster status
415
- if (currentProject) {
416
- setCurrentProject(currentProject);
417
- }
418
- }, 1000);
419
- }}
420
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800"
421
- >
422
- <Plus className="w-5 h-5 rotate-45" />
423
- </button>
424
- </div>
425
-
426
- {/* Terminal Container */}
427
- <div className="flex-1 p-4">
428
- <div
429
- className="h-full bg-black rounded-lg overflow-hidden"
430
- onClick={(e) => {
431
- // Focus the terminal when clicked
432
- const terminalElement = e.currentTarget.querySelector('.xterm-screen');
433
- if (terminalElement) {
434
- terminalElement.focus();
435
- }
436
- }}
437
- >
438
- <Shell
439
- selectedProject={currentProject}
440
- selectedSession={null}
441
- isActive={true}
442
- initialCommand="npx task-master init"
443
- isPlainShell={true}
444
- onProcessComplete={(exitCode) => {
445
- setIsTaskMasterComplete(true);
446
- if (exitCode === 0) {
447
- // Auto-refresh after successful completion
448
- setTimeout(() => {
449
- refreshProjects();
450
- if (currentProject) {
451
- setCurrentProject(currentProject);
452
- }
453
- }, 1000);
454
- }
455
- }}
456
- />
457
- </div>
458
- </div>
459
-
460
- {/* Modal Footer */}
461
- <div className="p-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
462
- <div className="flex items-center justify-between">
463
- <div className="text-sm text-gray-600 dark:text-gray-400">
464
- {isTaskMasterComplete ? (
465
- <span className="flex items-center gap-2 text-green-600 dark:text-green-400">
466
- <div className="w-2 h-2 bg-green-500 rounded-full"></div>
467
- TaskMaster setup completed! You can now close this window.
468
- </span>
469
- ) : (
470
- "TaskMaster initialization will start automatically"
471
- )}
472
- </div>
473
- <button
474
- onClick={() => {
475
- setShowCLI(false);
476
- setIsTaskMasterComplete(false); // Reset state
477
- // Refresh project data after closing CLI to detect TaskMaster initialization
478
- setTimeout(() => {
479
- refreshProjects();
480
- // Also refresh the current project's TaskMaster status
481
- if (currentProject) {
482
- setCurrentProject(currentProject);
483
- }
484
- }, 1000);
485
- }}
486
- className={cn(
487
- "px-4 py-2 text-sm font-medium rounded-md transition-colors",
488
- isTaskMasterComplete
489
- ? "bg-green-600 hover:bg-green-700 text-white"
490
- : "text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-600"
491
- )}
492
- >
493
- {isTaskMasterComplete ? "Close & Continue" : "Close"}
494
- </button>
495
- </div>
496
- </div>
497
- </div>
498
- </div>
499
- )}
500
- </div>
501
- );
502
- }
503
-
504
- return (
505
- <div className={cn('space-y-4', className)}>
506
- {/* Header Controls */}
507
- <div className="flex flex-col lg:flex-row gap-3 lg:items-center lg:justify-between">
508
- {/* Search Bar */}
509
- <div className="relative flex-1 max-w-md">
510
- <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
511
- <input
512
- type="text"
513
- placeholder="Search tasks..."
514
- value={searchTerm}
515
- onChange={(e) => setSearchTerm(e.target.value)}
516
- className="pl-10 pr-4 py-2 w-full border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
517
- />
518
- </div>
519
-
520
- {/* Controls */}
521
- <div className="flex flex-wrap items-center gap-2">
522
- {/* View Toggle */}
523
- <div className="flex bg-gray-100 dark:bg-gray-800 rounded-lg p-1">
524
- <button
525
- onClick={() => setViewMode('kanban')}
526
- className={cn(
527
- 'p-2 rounded-md transition-colors',
528
- viewMode === 'kanban'
529
- ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
530
- : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
531
- )}
532
- title="Kanban view"
533
- >
534
- <Columns className="w-4 h-4" />
535
- </button>
536
- <button
537
- onClick={() => setViewMode('list')}
538
- className={cn(
539
- 'p-2 rounded-md transition-colors',
540
- viewMode === 'list'
541
- ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
542
- : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
543
- )}
544
- title="List view"
545
- >
546
- <List className="w-4 h-4" />
547
- </button>
548
- <button
549
- onClick={() => setViewMode('grid')}
550
- className={cn(
551
- 'p-2 rounded-md transition-colors',
552
- viewMode === 'grid'
553
- ? 'bg-white dark:bg-gray-700 text-gray-900 dark:text-white shadow-sm'
554
- : 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300'
555
- )}
556
- title="Grid view"
557
- >
558
- <Grid className="w-4 h-4" />
559
- </button>
560
- </div>
561
-
562
- {/* Filters Toggle */}
563
- <button
564
- onClick={() => setShowFilters(!showFilters)}
565
- className={cn(
566
- 'flex items-center gap-2 px-3 py-2 rounded-lg border transition-colors',
567
- showFilters
568
- ? 'bg-blue-50 dark:bg-blue-900 border-blue-200 dark:border-blue-700 text-blue-700 dark:text-blue-300'
569
- : 'bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700'
570
- )}
571
- >
572
- <Filter className="w-4 h-4" />
573
- <span className="hidden sm:inline">Filters</span>
574
- <ChevronDown className={cn('w-4 h-4 transition-transform', showFilters && 'rotate-180')} />
575
- </button>
576
-
577
- {/* Action Buttons */}
578
- {currentProject && (
579
- <>
580
- {/* Help Button */}
581
- <button
582
- onClick={() => setShowHelpGuide(true)}
583
- className="p-2 text-gray-600 dark:text-gray-400 hover:text-blue-600 dark:hover:text-blue-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors border border-gray-300 dark:border-gray-600"
584
- title="TaskMaster Getting Started Guide"
585
- >
586
- <HelpCircle className="w-4 h-4" />
587
- </button>
588
-
589
- {/* PRD Management */}
590
- <div className="relative">
591
- {existingPRDs.length > 0 ? (
592
- // Dropdown when PRDs exist
593
- <div className="relative">
594
- <button
595
- onClick={() => setShowPRDDropdown(!showPRDDropdown)}
596
- className="flex items-center gap-2 px-3 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors font-medium"
597
- title={`${existingPRDs.length} PRD${existingPRDs.length > 1 ? 's' : ''} available`}
598
- >
599
- <FileText className="w-4 h-4" />
600
- <span className="hidden sm:inline">PRDs</span>
601
- <span className="px-1.5 py-0.5 text-xs bg-purple-500 rounded-full min-w-[1.25rem] text-center">
602
- {existingPRDs.length}
603
- </span>
604
- <ChevronDown className={cn('w-3 h-3 transition-transform hidden sm:block', showPRDDropdown && 'rotate-180')} />
605
- </button>
606
-
607
- {showPRDDropdown && (
608
- <div className="absolute right-0 top-full mt-2 w-56 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-xl z-30">
609
- <div className="p-2">
610
- <button
611
- onClick={() => {
612
- onShowPRDEditor?.();
613
- setShowPRDDropdown(false);
614
- }}
615
- className="w-full text-left px-3 py-2 text-sm font-medium text-purple-700 dark:text-purple-300 hover:bg-purple-50 dark:hover:bg-purple-900/30 rounded flex items-center gap-2"
616
- >
617
- <Plus className="w-4 h-4" />
618
- Create New PRD
619
- </button>
620
- <div className="border-t border-gray-200 dark:border-gray-700 my-1"></div>
621
- <div className="text-xs text-gray-500 dark:text-gray-400 px-3 py-1 font-medium">Existing PRDs:</div>
622
- {existingPRDs.map((prd) => (
623
- <button
624
- key={prd.name}
625
- onClick={async () => {
626
- try {
627
- const response = await api.get(`/taskmaster/prd/${encodeURIComponent(currentProject.name)}/${encodeURIComponent(prd.name)}`);
628
- if (response.ok) {
629
- const prdData = await response.json();
630
- onShowPRDEditor?.({
631
- name: prd.name,
632
- content: prdData.content,
633
- isExisting: true
634
- });
635
- setShowPRDDropdown(false);
636
- }
637
- } catch (error) {
638
- console.error('Error loading PRD:', error);
639
- }
640
- }}
641
- className="w-full text-left px-3 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex items-center gap-2"
642
- title={`Modified: ${new Date(prd.modified).toLocaleDateString()}`}
643
- >
644
- <FileText className="w-4 h-4" />
645
- <span className="truncate">{prd.name}</span>
646
- </button>
647
- ))}
648
- </div>
649
- </div>
650
- )}
651
- </div>
652
- ) : (
653
- // Simple button when no PRDs exist
654
- <button
655
- onClick={() => {
656
- onShowPRDEditor?.();
657
- }}
658
- className="flex items-center gap-2 px-3 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transition-colors font-medium"
659
- title="Create Product Requirements Document"
660
- >
661
- <FileText className="w-4 h-4" />
662
- <span className="hidden sm:inline">Add PRD</span>
663
- </button>
664
- )}
665
- </div>
666
-
667
- {/* Add Task Button */}
668
- {((currentProject?.taskMasterConfigured || currentProject?.taskmaster?.hasTaskmaster || projectTaskMaster?.hasTaskmaster) || tasks.length > 0) && (
669
- <button
670
- onClick={() => setShowCreateModal(true)}
671
- className="flex items-center gap-2 px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
672
- title="Add a new task"
673
- >
674
- <Plus className="w-4 h-4" />
675
- <span className="hidden sm:inline">Add Task</span>
676
- </button>
677
- )}
678
- </>
679
- )}
680
- </div>
681
- </div>
682
-
683
- {/* Expanded Filters */}
684
- {showFilters && (
685
- <div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 space-y-4">
686
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
687
- {/* Status Filter */}
688
- <div>
689
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
690
- Status
691
- </label>
692
- <select
693
- value={statusFilter}
694
- onChange={(e) => setStatusFilter(e.target.value)}
695
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
696
- >
697
- <option value="all">All Statuses</option>
698
- {statuses.map(status => (
699
- <option key={status} value={status}>
700
- {status.charAt(0).toUpperCase() + status.slice(1).replace('-', ' ')}
701
- </option>
702
- ))}
703
- </select>
704
- </div>
705
-
706
- {/* Priority Filter */}
707
- <div>
708
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
709
- Priority
710
- </label>
711
- <select
712
- value={priorityFilter}
713
- onChange={(e) => setPriorityFilter(e.target.value)}
714
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
715
- >
716
- <option value="all">All Priorities</option>
717
- {priorities.map(priority => (
718
- <option key={priority} value={priority}>
719
- {priority.charAt(0).toUpperCase() + priority.slice(1)}
720
- </option>
721
- ))}
722
- </select>
723
- </div>
724
-
725
- {/* Sort By */}
726
- <div>
727
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
728
- Sort By
729
- </label>
730
- <select
731
- value={`${sortBy}-${sortOrder}`}
732
- onChange={(e) => {
733
- const [field, order] = e.target.value.split('-');
734
- setSortBy(field);
735
- setSortOrder(order);
736
- }}
737
- className="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500"
738
- >
739
- <option value="id-asc">ID (Ascending)</option>
740
- <option value="id-desc">ID (Descending)</option>
741
- <option value="title-asc">Title (A-Z)</option>
742
- <option value="title-desc">Title (Z-A)</option>
743
- <option value="status-asc">Status (Pending First)</option>
744
- <option value="status-desc">Status (Done First)</option>
745
- <option value="priority-asc">Priority (High First)</option>
746
- <option value="priority-desc">Priority (Low First)</option>
747
- </select>
748
- </div>
749
- </div>
750
-
751
- {/* Filter Actions */}
752
- <div className="flex items-center justify-between">
753
- <div className="text-sm text-gray-600 dark:text-gray-400">
754
- Showing {filteredAndSortedTasks.length} of {tasks.length} tasks
755
- </div>
756
- <button
757
- onClick={clearFilters}
758
- className="text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 font-medium"
759
- >
760
- Clear Filters
761
- </button>
762
- </div>
763
- </div>
764
- )}
765
-
766
- {/* Quick Sort Buttons */}
767
- <div className="flex flex-wrap gap-2">
768
- <button
769
- onClick={() => handleSortChange('id')}
770
- className={cn(
771
- 'flex items-center gap-1 px-3 py-1.5 rounded-md text-sm transition-colors',
772
- sortBy === 'id'
773
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
774
- : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
775
- )}
776
- >
777
- ID {getSortIcon('id')}
778
- </button>
779
- <button
780
- onClick={() => handleSortChange('status')}
781
- className={cn(
782
- 'flex items-center gap-1 px-3 py-1.5 rounded-md text-sm transition-colors',
783
- sortBy === 'status'
784
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
785
- : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
786
- )}
787
- >
788
- Status {getSortIcon('status')}
789
- </button>
790
- <button
791
- onClick={() => handleSortChange('priority')}
792
- className={cn(
793
- 'flex items-center gap-1 px-3 py-1.5 rounded-md text-sm transition-colors',
794
- sortBy === 'priority'
795
- ? 'bg-blue-100 dark:bg-blue-900 text-blue-700 dark:text-blue-300'
796
- : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
797
- )}
798
- >
799
- Priority {getSortIcon('priority')}
800
- </button>
801
- </div>
802
-
803
- {/* Task Cards */}
804
- {filteredAndSortedTasks.length === 0 ? (
805
- <div className="text-center py-12">
806
- <div className="text-gray-500 dark:text-gray-400">
807
- <Search className="w-12 h-12 mx-auto mb-4 opacity-50" />
808
- <h3 className="text-lg font-medium mb-2">No tasks match your filters</h3>
809
- <p className="text-sm">Try adjusting your search or filter criteria.</p>
810
- </div>
811
- </div>
812
- ) : viewMode === 'kanban' ? (
813
- /* Kanban Board Layout - Dynamic grid based on column count */
814
- <div className={cn(
815
- "grid gap-6",
816
- kanbanColumns.length === 1 && "grid-cols-1 max-w-md mx-auto",
817
- kanbanColumns.length === 2 && "grid-cols-1 md:grid-cols-2",
818
- kanbanColumns.length === 3 && "grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
819
- kanbanColumns.length === 4 && "grid-cols-1 md:grid-cols-2 lg:grid-cols-4",
820
- kanbanColumns.length === 5 && "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",
821
- kanbanColumns.length >= 6 && "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
822
- )}>
823
- {kanbanColumns.map((column) => (
824
- <div key={column.id} className={cn('rounded-xl border shadow-sm transition-shadow hover:shadow-md', column.color)}>
825
- {/* Column Header */}
826
- <div className={cn('px-4 py-3 rounded-t-xl border-b', column.headerColor)}>
827
- <div className="flex items-center justify-between">
828
- <h3 className="font-semibold text-sm">
829
- {column.title}
830
- </h3>
831
- <div className="flex items-center gap-2">
832
- <span className="text-xs font-medium px-2 py-1 bg-white/60 dark:bg-black/20 rounded-full">
833
- {column.tasks.length}
834
- </span>
835
- </div>
836
- </div>
837
- </div>
838
-
839
- {/* Column Tasks */}
840
- <div className="p-3 space-y-3 min-h-[200px] max-h-[calc(100vh-300px)] overflow-y-auto">
841
- {column.tasks.length === 0 ? (
842
- <div className="text-center py-8 text-gray-400 dark:text-gray-500">
843
- <div className="w-8 h-8 mx-auto mb-2 rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
844
- <div className="w-3 h-3 rounded-full bg-gray-300 dark:bg-gray-600"></div>
845
- </div>
846
- <div className="text-xs font-medium text-gray-500 dark:text-gray-400">
847
- No tasks yet
848
- </div>
849
- <div className="text-xs text-gray-400 dark:text-gray-500 mt-1">
850
- {column.status === 'pending' ? 'Tasks will appear here' :
851
- column.status === 'in-progress' ? 'Move tasks here when started' :
852
- column.status === 'done' ? 'Completed tasks appear here' :
853
- 'Tasks with this status will appear here'}
854
- </div>
855
- </div>
856
- ) : (
857
- column.tasks.map((task) => (
858
- <div key={task.id} className="transform transition-transform hover:scale-[1.02]">
859
- <TaskCard
860
- task={task}
861
- onClick={() => onTaskClick?.(task)}
862
- showParent={showParentTasks}
863
- className="w-full shadow-sm hover:shadow-md transition-shadow cursor-pointer"
864
- />
865
- </div>
866
- ))
867
- )}
868
- </div>
869
- </div>
870
- ))}
871
- </div>
872
- ) : (
873
- <div className={cn(
874
- 'gap-4',
875
- viewMode === 'grid'
876
- ? 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3'
877
- : 'space-y-4'
878
- )}>
879
- {filteredAndSortedTasks.map((task) => (
880
- <TaskCard
881
- key={task.id}
882
- task={task}
883
- onClick={() => onTaskClick?.(task)}
884
- showParent={showParentTasks}
885
- className={viewMode === 'grid' ? 'h-full' : ''}
886
- />
887
- ))}
888
- </div>
889
- )}
890
-
891
- {/* Create Task Modal */}
892
- {showCreateModal && (
893
- <CreateTaskModal
894
- currentProject={currentProject}
895
- onClose={() => setShowCreateModal(false)}
896
- onTaskCreated={() => {
897
- setShowCreateModal(false);
898
- if (onTaskCreated) onTaskCreated();
899
- }}
900
- />
901
- )}
902
-
903
- {/* Help Guide Modal */}
904
- {showHelpGuide && (
905
- <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
906
- <div className="bg-white dark:bg-gray-900 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 w-full max-w-4xl max-h-[90vh] overflow-hidden">
907
- {/* Modal Header */}
908
- <div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
909
- <div className="flex items-center gap-3">
910
- <div className="w-10 h-10 bg-blue-100 dark:bg-blue-900/50 rounded-lg flex items-center justify-center">
911
- <FileText className="w-5 h-5 text-blue-600 dark:text-blue-400" />
912
- </div>
913
- <div>
914
- <h2 className="text-xl font-semibold text-gray-900 dark:text-white">Getting Started with TaskMaster</h2>
915
- <p className="text-sm text-gray-600 dark:text-gray-400">Your guide to productive task management</p>
916
- </div>
917
- </div>
918
- <button
919
- onClick={() => setShowHelpGuide(false)}
920
- className="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
921
- >
922
- <X className="w-5 h-5" />
923
- </button>
924
- </div>
925
-
926
- {/* Modal Content */}
927
- <div className="p-6 overflow-y-auto max-h-[calc(90vh-120px)]">
928
- <div className="space-y-4">
929
- {/* Step 1 */}
930
- <div className="flex gap-4 p-4 bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950/50 dark:to-indigo-950/50 rounded-lg border border-blue-200 dark:border-blue-800">
931
- <div className="flex-shrink-0 w-8 h-8 bg-blue-600 text-white text-sm font-semibold rounded-full flex items-center justify-center">1</div>
932
- <div>
933
- <h4 className="font-medium text-gray-900 dark:text-white mb-2">Create a Product Requirements Document (PRD)</h4>
934
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-3">Discuss your project idea and create a PRD that describes what you want to build.</p>
935
- <button
936
- onClick={() => {
937
- onShowPRDEditor?.();
938
- setShowHelpGuide(false);
939
- }}
940
- className="inline-flex items-center gap-2 text-sm bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 px-3 py-1.5 rounded-lg hover:bg-purple-200 dark:hover:bg-purple-900/50 transition-colors"
941
- >
942
- <FileText className="w-4 h-4" />
943
- Add PRD
944
- </button>
945
- </div>
946
- </div>
947
-
948
- {/* Step 2 */}
949
- <div className="flex gap-4 p-4 bg-gradient-to-r from-green-50 to-emerald-50 dark:from-green-950/50 dark:to-emerald-950/50 rounded-lg border border-green-200 dark:border-green-800">
950
- <div className="flex-shrink-0 w-8 h-8 bg-green-600 text-white text-sm font-semibold rounded-full flex items-center justify-center">2</div>
951
- <div>
952
- <h4 className="font-medium text-gray-900 dark:text-white mb-2">Generate Tasks from PRD</h4>
953
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-3">Once you have a PRD, ask your AI assistant to parse it and TaskMaster will automatically break it down into manageable tasks with implementation details.</p>
954
- <div className="bg-white dark:bg-gray-800/50 rounded border border-green-200 dark:border-green-700/50 p-3 mb-2">
955
- <p className="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">💬 Example:</p>
956
- <p className="text-xs text-gray-900 dark:text-white font-mono">
957
- "I've just initialized a new project with Claude Task Master. I have a PRD at .taskmaster/docs/prd.txt. Can you help me parse it and set up the initial tasks?"
958
- </p>
959
- </div>
960
- </div>
961
- </div>
962
-
963
- {/* Step 3 */}
964
- <div className="flex gap-4 p-4 bg-gradient-to-r from-amber-50 to-orange-50 dark:from-amber-950/50 dark:to-orange-950/50 rounded-lg border border-amber-200 dark:border-amber-800">
965
- <div className="flex-shrink-0 w-8 h-8 bg-amber-600 text-white text-sm font-semibold rounded-full flex items-center justify-center">3</div>
966
- <div>
967
- <h4 className="font-medium text-gray-900 dark:text-white mb-2">Analyze & Expand Tasks</h4>
968
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-3">Ask your AI assistant to analyze task complexity and expand them into detailed subtasks for easier implementation.</p>
969
- <div className="bg-white dark:bg-gray-800/50 rounded border border-amber-200 dark:border-amber-700/50 p-3 mb-2">
970
- <p className="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">💬 Example:</p>
971
- <p className="text-xs text-gray-900 dark:text-white font-mono">
972
- "Task 5 seems complex. Can you break it down into subtasks?"
973
- </p>
974
- </div>
975
- </div>
976
- </div>
977
-
978
- {/* Step 4 */}
979
- <div className="flex gap-4 p-4 bg-gradient-to-r from-purple-50 to-pink-50 dark:from-purple-950/50 dark:to-pink-950/50 rounded-lg border border-purple-200 dark:border-purple-800">
980
- <div className="flex-shrink-0 w-8 h-8 bg-purple-600 text-white text-sm font-semibold rounded-full flex items-center justify-center">4</div>
981
- <div>
982
- <h4 className="font-medium text-gray-900 dark:text-white mb-2">Start Building</h4>
983
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-3">Ask your AI assistant to begin working on tasks, update their status, and add new tasks as your project evolves.</p>
984
- <div className="bg-white dark:bg-gray-800/50 rounded border border-purple-200 dark:border-purple-700/50 p-3 mb-3">
985
- <p className="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">💬 Example:</p>
986
- <p className="text-xs text-gray-900 dark:text-white font-mono">
987
- "Please add a new task to implement user profile image uploads using Cloudinary, research the best approach."
988
- </p>
989
- </div>
990
- <a
991
- href="https://github.com/eyaltoledano/claude-task-master/blob/main/docs/examples.md"
992
- target="_blank"
993
- rel="noopener noreferrer"
994
- className="inline-block text-xs text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 underline"
995
- >
996
- View more examples and usage patterns →
997
- </a>
998
- </div>
999
- </div>
1000
-
1001
- {/* Pro Tips */}
1002
- <div className="mt-6 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700">
1003
- <h4 className="font-medium text-gray-900 dark:text-white mb-3">💡 Pro Tips</h4>
1004
- <ul className="space-y-2 text-sm text-gray-600 dark:text-gray-400">
1005
- <li className="flex items-start gap-2">
1006
- <span className="w-1.5 h-1.5 bg-blue-500 rounded-full mt-2 flex-shrink-0"></span>
1007
- Use the search bar to quickly find specific tasks
1008
- </li>
1009
- <li className="flex items-start gap-2">
1010
- <span className="w-1.5 h-1.5 bg-green-500 rounded-full mt-2 flex-shrink-0"></span>
1011
- Switch between Kanban, List, and Grid views using the view toggles
1012
- </li>
1013
- <li className="flex items-start gap-2">
1014
- <span className="w-1.5 h-1.5 bg-purple-500 rounded-full mt-2 flex-shrink-0"></span>
1015
- Use filters to focus on specific task statuses or priorities
1016
- </li>
1017
- <li className="flex items-start gap-2">
1018
- <span className="w-1.5 h-1.5 bg-orange-500 rounded-full mt-2 flex-shrink-0"></span>
1019
- Click on any task to view detailed information and manage subtasks
1020
- </li>
1021
- </ul>
1022
- </div>
1023
-
1024
- {/* Learn More Section */}
1025
- <div className="mt-6 p-4 bg-blue-50 dark:bg-blue-950/50 rounded-lg border border-blue-200 dark:border-blue-800">
1026
- <h4 className="font-medium text-blue-900 dark:text-blue-100 mb-3">📚 Learn More</h4>
1027
- <p className="text-sm text-blue-800 dark:text-blue-200 mb-3">
1028
- TaskMaster AI is an advanced task management system built for developers. Get documentation, examples, and contribute to the project.
1029
- </p>
1030
- <a
1031
- href="https://github.com/eyaltoledano/claude-task-master"
1032
- target="_blank"
1033
- rel="noopener noreferrer"
1034
- className="inline-flex items-center gap-2 text-sm bg-blue-600 hover:bg-blue-700 text-white px-3 py-2 rounded-lg font-medium transition-colors"
1035
- >
1036
- <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
1037
- <path fillRule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clipRule="evenodd" />
1038
- </svg>
1039
- View on GitHub
1040
- <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1041
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
1042
- </svg>
1043
- </a>
1044
- </div>
1045
- </div>
1046
- </div>
1047
- </div>
1048
- </div>
1049
- )}
1050
- </div>
1051
- );
1052
- };
1053
-
1054
- export default TaskList;