@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,695 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { ArrowRight, List, Clock, Flag, CheckCircle, Circle, AlertCircle, Pause, ChevronDown, ChevronUp, Plus, FileText, Settings, X, Terminal, Eye, Play, Zap, Target } from 'lucide-react';
3
- import { cn } from '../lib/utils';
4
- import { useTaskMaster } from '../contexts/TaskMasterContext';
5
- import { api } from '../utils/api';
6
- import Shell from './Shell';
7
- import TaskDetail from './TaskDetail';
8
-
9
- const NextTaskBanner = ({ onShowAllTasks, onStartTask, className = '' }) => {
10
- const { nextTask, tasks, currentProject, isLoadingTasks, projectTaskMaster, refreshTasks, refreshProjects } = useTaskMaster();
11
- const [showDetails, setShowDetails] = useState(false);
12
- const [showTaskOptions, setShowTaskOptions] = useState(false);
13
- const [showCreateTaskModal, setShowCreateTaskModal] = useState(false);
14
- const [showTemplateSelector, setShowTemplateSelector] = useState(false);
15
- const [showCLI, setShowCLI] = useState(false);
16
- const [showTaskDetail, setShowTaskDetail] = useState(false);
17
- const [isLoading, setIsLoading] = useState(false);
18
-
19
- // Handler functions
20
- const handleInitializeTaskMaster = async () => {
21
- if (!currentProject) return;
22
-
23
- setIsLoading(true);
24
- try {
25
- const response = await api.taskmaster.init(currentProject.name);
26
- if (response.ok) {
27
- await refreshProjects();
28
- setShowTaskOptions(false);
29
- } else {
30
- const error = await response.json();
31
- console.error('Failed to initialize TaskMaster:', error);
32
- alert(`Failed to initialize TaskMaster: ${error.message}`);
33
- }
34
- } catch (error) {
35
- console.error('Error initializing TaskMaster:', error);
36
- alert('Error initializing TaskMaster. Please try again.');
37
- } finally {
38
- setIsLoading(false);
39
- }
40
- };
41
-
42
- const handleCreateManualTask = () => {
43
- setShowCreateTaskModal(true);
44
- setShowTaskOptions(false);
45
- };
46
-
47
- const handleParsePRD = () => {
48
- setShowTemplateSelector(true);
49
- setShowTaskOptions(false);
50
- };
51
-
52
- // Don't show if no project or still loading
53
- if (!currentProject || isLoadingTasks) {
54
- return null;
55
- }
56
-
57
- let bannerContent;
58
-
59
- // Show setup message only if no tasks exist AND TaskMaster is not configured
60
- if ((!tasks || tasks.length === 0) && !projectTaskMaster?.hasTaskmaster) {
61
- bannerContent = (
62
- <div className={cn(
63
- 'bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 rounded-lg p-3 mb-4',
64
- className
65
- )}>
66
- <div className="flex items-center justify-between">
67
- <div className="flex items-center gap-2">
68
- <List className="w-4 h-4 text-blue-600 dark:text-blue-400" />
69
- <div>
70
- <div className="text-sm font-medium text-gray-900 dark:text-white">
71
- TaskMaster AI is not configured
72
- </div>
73
- <div className="text-xs text-gray-600 dark:text-gray-400 mt-0.5">
74
- </div>
75
- </div>
76
- </div>
77
- <div className="flex items-center gap-1">
78
- <button
79
- onClick={() => setShowTaskOptions(!showTaskOptions)}
80
- className="text-xs px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors flex items-center gap-1"
81
- >
82
- <Settings className="w-3 h-3" />
83
- Initialize TaskMaster AI
84
- </button>
85
- </div>
86
- </div>
87
-
88
- {showTaskOptions && (
89
- <div className="mt-3 pt-3 border-t border-blue-200 dark:border-blue-800">
90
- {!projectTaskMaster?.hasTaskmaster && (
91
- <div className="mb-3 p-3 bg-blue-50 dark:bg-blue-900/50 rounded-lg">
92
- <h4 className="text-sm font-medium text-blue-900 dark:text-blue-100 mb-2">
93
- 🎯 What is TaskMaster?
94
- </h4>
95
- <div className="text-xs text-blue-800 dark:text-blue-200 space-y-1">
96
- <p>• <strong>AI-Powered Task Management:</strong> Break complex projects into manageable subtasks</p>
97
- <p>• <strong>PRD Templates:</strong> Generate tasks from Product Requirements Documents</p>
98
- <p>• <strong>Dependency Tracking:</strong> Understand task relationships and execution order</p>
99
- <p>• <strong>Progress Visualization:</strong> Kanban boards and detailed task analytics</p>
100
- <p>• <strong>CLI Integration:</strong> Use taskmaster commands for advanced workflows</p>
101
- </div>
102
- </div>
103
- )}
104
- <div className="flex flex-col gap-2">
105
- {!projectTaskMaster?.hasTaskmaster ? (
106
- <button
107
- className="text-xs px-3 py-2 bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-slate-800 dark:text-slate-200 rounded transition-colors text-left flex items-center gap-2"
108
- onClick={() => setShowCLI(true)}
109
- >
110
- <Terminal className="w-3 h-3" />
111
- Initialize TaskMaster
112
- </button>
113
- ) : (
114
- <>
115
- <div className="mb-2 p-2 bg-green-50 dark:bg-green-900/30 rounded text-xs text-green-800 dark:text-green-200">
116
- <strong>Add more tasks:</strong> Create additional tasks manually or generate them from a PRD template
117
- </div>
118
- <button
119
- className="text-xs px-3 py-2 bg-green-100 dark:bg-green-900 hover:bg-green-200 dark:hover:bg-green-800 text-green-800 dark:text-green-200 rounded transition-colors text-left flex items-center gap-2 disabled:opacity-50"
120
- onClick={handleCreateManualTask}
121
- disabled={isLoading}
122
- >
123
- <Plus className="w-3 h-3" />
124
- Create a new task manually
125
- </button>
126
- <button
127
- className="text-xs px-3 py-2 bg-purple-100 dark:bg-purple-900 hover:bg-purple-200 dark:hover:bg-purple-800 text-purple-800 dark:text-purple-200 rounded transition-colors text-left flex items-center gap-2 disabled:opacity-50"
128
- onClick={handleParsePRD}
129
- disabled={isLoading}
130
- >
131
- <FileText className="w-3 h-3" />
132
- {isLoading ? 'Parsing...' : 'Generate tasks from PRD template'}
133
- </button>
134
- </>
135
- )}
136
- </div>
137
- </div>
138
- )}
139
- </div>
140
- );
141
- } else if (nextTask) {
142
- // Show next task if available
143
- bannerContent = (
144
- <div className={cn(
145
- 'bg-slate-50 dark:bg-slate-900/30 border border-slate-200 dark:border-slate-700 rounded-lg p-3 mb-4',
146
- className
147
- )}>
148
- <div className="flex items-center justify-between gap-3">
149
- <div className="flex-1 min-w-0">
150
- <div className="flex items-center gap-2 mb-1">
151
- <div className="w-5 h-5 bg-blue-100 dark:bg-blue-900/50 rounded-full flex items-center justify-center flex-shrink-0">
152
- <Target className="w-3 h-3 text-blue-600 dark:text-blue-400" />
153
- </div>
154
- <span className="text-xs text-slate-600 dark:text-slate-400 font-medium">Task {nextTask.id}</span>
155
- {nextTask.priority === 'high' && (
156
- <div className="w-4 h-4 rounded bg-red-100 dark:bg-red-900/50 flex items-center justify-center" title="High Priority">
157
- <Zap className="w-2.5 h-2.5 text-red-600 dark:text-red-400" />
158
- </div>
159
- )}
160
- {nextTask.priority === 'medium' && (
161
- <div className="w-4 h-4 rounded bg-amber-100 dark:bg-amber-900/50 flex items-center justify-center" title="Medium Priority">
162
- <Flag className="w-2.5 h-2.5 text-amber-600 dark:text-amber-400" />
163
- </div>
164
- )}
165
- {nextTask.priority === 'low' && (
166
- <div className="w-4 h-4 rounded bg-gray-100 dark:bg-gray-800 flex items-center justify-center" title="Low Priority">
167
- <Circle className="w-2.5 h-2.5 text-gray-400 dark:text-gray-500" />
168
- </div>
169
- )}
170
- </div>
171
- <p className="text-sm font-medium text-slate-900 dark:text-slate-100 line-clamp-1">
172
- {nextTask.title}
173
- </p>
174
- </div>
175
-
176
- <div className="flex items-center gap-1 flex-shrink-0">
177
- <button
178
- onClick={() => onStartTask?.()}
179
- className="text-xs px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white rounded-md font-medium transition-colors shadow-sm flex items-center gap-1"
180
- >
181
- <Play className="w-3 h-3" />
182
- Start Task
183
- </button>
184
- <button
185
- onClick={() => setShowTaskDetail(true)}
186
- className="text-xs px-2 py-1.5 border border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-300 rounded-md transition-colors flex items-center gap-1"
187
- title="View task details"
188
- >
189
- <Eye className="w-3 h-3" />
190
- </button>
191
- {onShowAllTasks && (
192
- <button
193
- onClick={onShowAllTasks}
194
- className="text-xs px-2 py-1.5 border border-slate-300 dark:border-slate-600 hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-300 rounded-md transition-colors flex items-center gap-1"
195
- title="View all tasks"
196
- >
197
- <List className="w-3 h-3" />
198
- </button>
199
- )}
200
- </div>
201
- </div>
202
-
203
- </div>
204
- );
205
- } else if (tasks && tasks.length > 0) {
206
- // Show completion message only if there are tasks and all are done
207
- const completedTasks = tasks.filter(task => task.status === 'done').length;
208
- const totalTasks = tasks.length;
209
-
210
- bannerContent = (
211
- <div className={cn(
212
- 'bg-purple-50 dark:bg-purple-950 border border-purple-200 dark:border-purple-800 rounded-lg p-3 mb-4',
213
- className
214
- )}>
215
- <div className="flex items-center justify-between">
216
- <div className="flex items-center gap-2">
217
- <CheckCircle className="w-4 h-4 text-purple-600 dark:text-purple-400" />
218
- <span className="text-sm font-medium text-gray-900 dark:text-white">
219
- {completedTasks === totalTasks ? "All done! 🎉" : "No pending tasks"}
220
- </span>
221
- </div>
222
- <div className="flex items-center gap-2">
223
- <span className="text-xs text-gray-600 dark:text-gray-400">
224
- {completedTasks}/{totalTasks}
225
- </span>
226
- <button
227
- onClick={onShowAllTasks}
228
- className="text-xs px-2 py-1 bg-purple-600 hover:bg-purple-700 text-white rounded transition-colors"
229
- >
230
- Review
231
- </button>
232
- </div>
233
- </div>
234
- </div>
235
- );
236
- } else {
237
- // TaskMaster is configured but no tasks exist - don't show anything in chat
238
- bannerContent = null;
239
- }
240
-
241
- return (
242
- <>
243
- {bannerContent}
244
-
245
- {/* Create Task Modal */}
246
- {showCreateTaskModal && (
247
- <CreateTaskModal
248
- currentProject={currentProject}
249
- onClose={() => setShowCreateTaskModal(false)}
250
- onTaskCreated={() => {
251
- refreshTasks();
252
- setShowCreateTaskModal(false);
253
- }}
254
- />
255
- )}
256
-
257
- {/* Template Selector Modal */}
258
- {showTemplateSelector && (
259
- <TemplateSelector
260
- currentProject={currentProject}
261
- onClose={() => setShowTemplateSelector(false)}
262
- onTemplateApplied={() => {
263
- refreshTasks();
264
- setShowTemplateSelector(false);
265
- }}
266
- />
267
- )}
268
-
269
- {/* TaskMaster CLI Setup Modal */}
270
- {showCLI && (
271
- <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
272
- <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">
273
- {/* Modal Header */}
274
- <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
275
- <div className="flex items-center gap-3">
276
- <div className="w-8 h-8 bg-blue-100 dark:bg-blue-900/50 rounded-lg flex items-center justify-center">
277
- <Terminal className="w-4 h-4 text-blue-600 dark:text-blue-400" />
278
- </div>
279
- <div>
280
- <h2 className="text-lg font-semibold text-gray-900 dark:text-white">TaskMaster Setup</h2>
281
- <p className="text-sm text-gray-500 dark:text-gray-400">Interactive CLI for {currentProject?.displayName}</p>
282
- </div>
283
- </div>
284
- <button
285
- onClick={() => setShowCLI(false)}
286
- 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"
287
- >
288
- <X className="w-5 h-5" />
289
- </button>
290
- </div>
291
-
292
- {/* Terminal Container */}
293
- <div className="flex-1 p-4">
294
- <div className="h-full bg-black rounded-lg overflow-hidden">
295
- <Shell
296
- selectedProject={currentProject}
297
- selectedSession={null}
298
- isActive={true}
299
- initialCommand="npx task-master init"
300
- isPlainShell={true}
301
- />
302
- </div>
303
- </div>
304
-
305
- {/* Modal Footer */}
306
- <div className="p-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50">
307
- <div className="flex items-center justify-between">
308
- <div className="text-sm text-gray-600 dark:text-gray-400">
309
- TaskMaster initialization will start automatically
310
- </div>
311
- <button
312
- onClick={() => setShowCLI(false)}
313
- className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
314
- >
315
- Close
316
- </button>
317
- </div>
318
- </div>
319
- </div>
320
- </div>
321
- )}
322
-
323
- {/* Task Detail Modal */}
324
- {showTaskDetail && nextTask && (
325
- <TaskDetail
326
- task={nextTask}
327
- isOpen={showTaskDetail}
328
- onClose={() => setShowTaskDetail(false)}
329
- onStatusChange={() => refreshTasks?.()}
330
- onTaskClick={null} // Disable dependency navigation in NextTaskBanner for now
331
- />
332
- )}
333
- </>
334
- );
335
- };
336
-
337
- // Simple Create Task Modal Component
338
- const CreateTaskModal = ({ currentProject, onClose, onTaskCreated }) => {
339
- const [formData, setFormData] = useState({
340
- title: '',
341
- description: '',
342
- priority: 'medium',
343
- useAI: false,
344
- prompt: ''
345
- });
346
- const [isSubmitting, setIsSubmitting] = useState(false);
347
-
348
- const handleSubmit = async (e) => {
349
- e.preventDefault();
350
- if (!currentProject) return;
351
-
352
- setIsSubmitting(true);
353
- try {
354
- const taskData = formData.useAI
355
- ? { prompt: formData.prompt, priority: formData.priority }
356
- : { title: formData.title, description: formData.description, priority: formData.priority };
357
-
358
- const response = await api.taskmaster.addTask(currentProject.name, taskData);
359
-
360
- if (response.ok) {
361
- onTaskCreated();
362
- } else {
363
- const error = await response.json();
364
- console.error('Failed to create task:', error);
365
- alert(`Failed to create task: ${error.message}`);
366
- }
367
- } catch (error) {
368
- console.error('Error creating task:', error);
369
- alert('Error creating task. Please try again.');
370
- } finally {
371
- setIsSubmitting(false);
372
- }
373
- };
374
-
375
- return (
376
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
377
- <div className="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-md">
378
- <div className="flex items-center justify-between mb-4">
379
- <h3 className="text-lg font-semibold text-gray-900 dark:text-white">Create New Task</h3>
380
- <button
381
- onClick={onClose}
382
- className="p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded"
383
- >
384
- <X className="w-4 h-4" />
385
- </button>
386
- </div>
387
-
388
- <form onSubmit={handleSubmit} className="space-y-4">
389
- <div>
390
- <label className="flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
391
- <input
392
- type="checkbox"
393
- checked={formData.useAI}
394
- onChange={(e) => setFormData(prev => ({ ...prev, useAI: e.target.checked }))}
395
- />
396
- Use AI to generate task details
397
- </label>
398
- </div>
399
-
400
- {formData.useAI ? (
401
- <div>
402
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
403
- Task Description (AI will generate details)
404
- </label>
405
- <textarea
406
- value={formData.prompt}
407
- onChange={(e) => setFormData(prev => ({ ...prev, prompt: e.target.value }))}
408
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
409
- rows="3"
410
- placeholder="Describe what you want to accomplish..."
411
- required
412
- />
413
- </div>
414
- ) : (
415
- <>
416
- <div>
417
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
418
- Task Title
419
- </label>
420
- <input
421
- type="text"
422
- value={formData.title}
423
- onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
424
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
425
- placeholder="Enter task title..."
426
- required
427
- />
428
- </div>
429
-
430
- <div>
431
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
432
- Description
433
- </label>
434
- <textarea
435
- value={formData.description}
436
- onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
437
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
438
- rows="3"
439
- placeholder="Describe the task..."
440
- required
441
- />
442
- </div>
443
- </>
444
- )}
445
-
446
- <div>
447
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
448
- Priority
449
- </label>
450
- <select
451
- value={formData.priority}
452
- onChange={(e) => setFormData(prev => ({ ...prev, priority: e.target.value }))}
453
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
454
- >
455
- <option value="low">Low</option>
456
- <option value="medium">Medium</option>
457
- <option value="high">High</option>
458
- </select>
459
- </div>
460
-
461
- <div className="flex gap-2 pt-4">
462
- <button
463
- type="button"
464
- onClick={onClose}
465
- className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
466
- disabled={isSubmitting}
467
- >
468
- Cancel
469
- </button>
470
- <button
471
- type="submit"
472
- className="flex-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded disabled:opacity-50"
473
- disabled={isSubmitting || (formData.useAI && !formData.prompt.trim()) || (!formData.useAI && (!formData.title.trim() || !formData.description.trim()))}
474
- >
475
- {isSubmitting ? 'Creating...' : 'Create Task'}
476
- </button>
477
- </div>
478
- </form>
479
- </div>
480
- </div>
481
- );
482
- };
483
-
484
- // Template Selector Modal Component
485
- const TemplateSelector = ({ currentProject, onClose, onTemplateApplied }) => {
486
- const [templates, setTemplates] = useState([]);
487
- const [selectedTemplate, setSelectedTemplate] = useState(null);
488
- const [customizations, setCustomizations] = useState({});
489
- const [fileName, setFileName] = useState('prd.txt');
490
- const [isLoading, setIsLoading] = useState(true);
491
- const [isApplying, setIsApplying] = useState(false);
492
- const [step, setStep] = useState('select'); // 'select', 'customize', 'generate'
493
-
494
- useEffect(() => {
495
- const loadTemplates = async () => {
496
- try {
497
- const response = await api.taskmaster.getTemplates();
498
- if (response.ok) {
499
- const data = await response.json();
500
- setTemplates(data.templates);
501
- }
502
- } catch (error) {
503
- console.error('Error loading templates:', error);
504
- } finally {
505
- setIsLoading(false);
506
- }
507
- };
508
-
509
- loadTemplates();
510
- }, []);
511
-
512
- const handleSelectTemplate = (template) => {
513
- setSelectedTemplate(template);
514
- // Find placeholders in template content
515
- const placeholders = template.content.match(/\[([^\]]+)\]/g) || [];
516
- const uniquePlaceholders = [...new Set(placeholders.map(p => p.slice(1, -1)))];
517
-
518
- const initialCustomizations = {};
519
- uniquePlaceholders.forEach(placeholder => {
520
- initialCustomizations[placeholder] = '';
521
- });
522
-
523
- setCustomizations(initialCustomizations);
524
- setStep('customize');
525
- };
526
-
527
- const handleApplyTemplate = async () => {
528
- if (!selectedTemplate || !currentProject) return;
529
-
530
- setIsApplying(true);
531
- try {
532
- // Apply template
533
- const applyResponse = await api.taskmaster.applyTemplate(currentProject.name, {
534
- templateId: selectedTemplate.id,
535
- fileName,
536
- customizations
537
- });
538
-
539
- if (!applyResponse.ok) {
540
- const error = await applyResponse.json();
541
- throw new Error(error.message || 'Failed to apply template');
542
- }
543
-
544
- // Parse PRD to generate tasks
545
- const parseResponse = await api.taskmaster.parsePRD(currentProject.name, {
546
- fileName,
547
- numTasks: 10
548
- });
549
-
550
- if (!parseResponse.ok) {
551
- const error = await parseResponse.json();
552
- throw new Error(error.message || 'Failed to generate tasks');
553
- }
554
-
555
- setStep('generate');
556
- setTimeout(() => {
557
- onTemplateApplied();
558
- }, 2000);
559
-
560
- } catch (error) {
561
- console.error('Error applying template:', error);
562
- alert(`Error: ${error.message}`);
563
- setIsApplying(false);
564
- }
565
- };
566
-
567
- if (isLoading) {
568
- return (
569
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
570
- <div className="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-md">
571
- <div className="flex items-center gap-3">
572
- <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
573
- <span className="text-gray-900 dark:text-white">Loading templates...</span>
574
- </div>
575
- </div>
576
- </div>
577
- );
578
- }
579
-
580
- return (
581
- <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
582
- <div className="bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-2xl max-h-[80vh] overflow-y-auto">
583
- <div className="flex items-center justify-between mb-4">
584
- <h3 className="text-lg font-semibold text-gray-900 dark:text-white">
585
- {step === 'select' ? 'Select PRD Template' :
586
- step === 'customize' ? 'Customize Template' :
587
- 'Generating Tasks'}
588
- </h3>
589
- <button
590
- onClick={onClose}
591
- className="p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded"
592
- >
593
- <X className="w-4 h-4" />
594
- </button>
595
- </div>
596
-
597
- {step === 'select' && (
598
- <div className="space-y-3">
599
- {templates.map((template) => (
600
- <div
601
- key={template.id}
602
- className="p-4 border border-gray-200 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors"
603
- onClick={() => handleSelectTemplate(template)}
604
- >
605
- <div className="flex items-start justify-between">
606
- <div className="flex-1">
607
- <h4 className="font-medium text-gray-900 dark:text-white">{template.name}</h4>
608
- <p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{template.description}</p>
609
- <span className="inline-block text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 rounded mt-2">
610
- {template.category}
611
- </span>
612
- </div>
613
- <ArrowRight className="w-4 h-4 text-gray-400 mt-1" />
614
- </div>
615
- </div>
616
- ))}
617
- </div>
618
- )}
619
-
620
- {step === 'customize' && selectedTemplate && (
621
- <div className="space-y-4">
622
- <div>
623
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
624
- File Name
625
- </label>
626
- <input
627
- type="text"
628
- value={fileName}
629
- onChange={(e) => setFileName(e.target.value)}
630
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
631
- placeholder="prd.txt"
632
- />
633
- </div>
634
-
635
- {Object.keys(customizations).length > 0 && (
636
- <div>
637
- <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
638
- Customize Template
639
- </label>
640
- <div className="space-y-3">
641
- {Object.entries(customizations).map(([key, value]) => (
642
- <div key={key}>
643
- <label className="block text-xs text-gray-600 dark:text-gray-400 mb-1">
644
- {key.replace(/([A-Z])/g, ' $1').replace(/^./, str => str.toUpperCase())}
645
- </label>
646
- <input
647
- type="text"
648
- value={value}
649
- onChange={(e) => setCustomizations(prev => ({ ...prev, [key]: e.target.value }))}
650
- className="w-full p-2 border border-gray-300 dark:border-gray-600 rounded text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
651
- placeholder={`Enter ${key.toLowerCase()}`}
652
- />
653
- </div>
654
- ))}
655
- </div>
656
- </div>
657
- )}
658
-
659
- <div className="flex gap-2 pt-4">
660
- <button
661
- onClick={() => setStep('select')}
662
- className="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300"
663
- >
664
- Back
665
- </button>
666
- <button
667
- onClick={handleApplyTemplate}
668
- className="flex-1 px-4 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded disabled:opacity-50"
669
- disabled={isApplying}
670
- >
671
- {isApplying ? 'Applying...' : 'Apply & Generate Tasks'}
672
- </button>
673
- </div>
674
- </div>
675
- )}
676
-
677
- {step === 'generate' && (
678
- <div className="text-center py-8">
679
- <div className="w-16 h-16 bg-green-100 dark:bg-green-900 rounded-full flex items-center justify-center mx-auto mb-4">
680
- <CheckCircle className="w-8 h-8 text-green-600 dark:text-green-400" />
681
- </div>
682
- <h4 className="text-lg font-medium text-gray-900 dark:text-white mb-2">
683
- Template Applied Successfully!
684
- </h4>
685
- <p className="text-gray-600 dark:text-gray-400">
686
- Your PRD has been created and tasks are being generated...
687
- </p>
688
- </div>
689
- )}
690
- </div>
691
- </div>
692
- );
693
- };
694
-
695
- export default NextTaskBanner;