@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,871 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
- import CodeMirror from '@uiw/react-codemirror';
3
- import { markdown } from '@codemirror/lang-markdown';
4
- import { oneDark } from '@codemirror/theme-one-dark';
5
- import { EditorView } from '@codemirror/view';
6
- import { X, Save, Download, Maximize2, Minimize2, Eye, FileText, Sparkles, AlertTriangle } from 'lucide-react';
7
- import { cn } from '../lib/utils';
8
- import { api, authenticatedFetch } from '../utils/api';
9
-
10
- const PRDEditor = ({
11
- file,
12
- onClose,
13
- projectPath,
14
- project, // Add project object
15
- initialContent = '',
16
- isNewFile = false,
17
- onSave
18
- }) => {
19
- const [content, setContent] = useState(initialContent);
20
- const [loading, setLoading] = useState(!isNewFile);
21
- const [saving, setSaving] = useState(false);
22
- const [isFullscreen, setIsFullscreen] = useState(false);
23
- const [isDarkMode, setIsDarkMode] = useState(true);
24
- const [saveSuccess, setSaveSuccess] = useState(false);
25
- const [previewMode, setPreviewMode] = useState(false);
26
- const [wordWrap, setWordWrap] = useState(true); // Default to true for markdown
27
- const [fileName, setFileName] = useState('');
28
- const [showGenerateModal, setShowGenerateModal] = useState(false);
29
- const [showOverwriteConfirm, setShowOverwriteConfirm] = useState(false);
30
- const [existingPRDs, setExistingPRDs] = useState([]);
31
-
32
- const editorRef = useRef(null);
33
-
34
- const PRD_TEMPLATE = `# Product Requirements Document - Example Project
35
-
36
- ## 1. Overview
37
- **Product Name:** AI-Powered Task Manager
38
- **Version:** 1.0
39
- **Date:** 2024-12-27
40
- **Author:** Development Team
41
-
42
- This document outlines the requirements for building an AI-powered task management application that integrates with development workflows and provides intelligent task breakdown and prioritization.
43
-
44
- ## 2. Objectives
45
- - Create an intuitive task management system that works seamlessly with developer tools
46
- - Provide AI-powered task generation from high-level requirements
47
- - Enable real-time collaboration and progress tracking
48
- - Integrate with popular development environments (VS Code, Cursor, etc.)
49
-
50
- ### Success Metrics
51
- - User adoption rate > 80% within development teams
52
- - Task completion rate improvement of 25%
53
- - Time-to-delivery reduction of 15%
54
-
55
- ## 3. User Stories
56
-
57
- ### Core Functionality
58
- - As a project manager, I want to create PRDs that automatically generate detailed tasks so I can save time on project planning
59
- - As a developer, I want to see my next task clearly highlighted so I can maintain focus
60
- - As a team lead, I want to track progress across multiple projects so I can provide accurate status updates
61
- - As a developer, I want tasks to be broken down into implementable subtasks so I can work more efficiently
62
-
63
- ### AI Integration
64
- - As a user, I want to describe a feature in natural language and get detailed implementation tasks so I can start working immediately
65
- - As a project manager, I want the AI to analyze task complexity and suggest appropriate time estimates
66
- - As a developer, I want intelligent task prioritization based on dependencies and deadlines
67
-
68
- ### Collaboration
69
- - As a team member, I want to see real-time updates when tasks are completed so I can coordinate my work
70
- - As a stakeholder, I want to view project progress through intuitive dashboards
71
- - As a developer, I want to add implementation notes to tasks for future reference
72
-
73
- ## 4. Functional Requirements
74
-
75
- ### Task Management
76
- - Create, edit, and delete tasks with rich metadata (priority, status, dependencies, estimates)
77
- - Hierarchical task structure with subtasks and sub-subtasks
78
- - Real-time status updates and progress tracking
79
- - Dependency management with circular dependency detection
80
- - Bulk operations (move, update status, assign)
81
-
82
- ### AI Features
83
- - Natural language PRD parsing to generate structured tasks
84
- - Intelligent task breakdown with complexity analysis
85
- - Automated subtask generation with implementation details
86
- - Smart dependency suggestion
87
- - Progress prediction based on historical data
88
-
89
- ### Integration Features
90
- - VS Code/Cursor extension for in-editor task management
91
- - Git integration for linking commits to tasks
92
- - API for third-party tool integration
93
- - Webhook support for external notifications
94
- - CLI tool for command-line task management
95
-
96
- ### User Interface
97
- - Responsive web application (desktop and mobile)
98
- - Multiple view modes (Kanban, list, calendar)
99
- - Dark/light theme support
100
- - Drag-and-drop task organization
101
- - Advanced filtering and search capabilities
102
- - Keyboard shortcuts for power users
103
-
104
- ## 5. Technical Requirements
105
-
106
- ### Frontend
107
- - React.js with TypeScript for type safety
108
- - Modern UI framework (Tailwind CSS)
109
- - State management (Context API or Redux)
110
- - Real-time updates via WebSockets
111
- - Progressive Web App (PWA) support
112
- - Accessibility compliance (WCAG 2.1 AA)
113
-
114
- ### Backend
115
- - Node.js with Express.js framework
116
- - RESTful API design with OpenAPI documentation
117
- - Real-time communication via Socket.io
118
- - Background job processing
119
- - Rate limiting and security middleware
120
-
121
- ### AI Integration
122
- - Integration with multiple AI providers (OpenAI, Anthropic, etc.)
123
- - Fallback model support
124
- - Context-aware prompt engineering
125
- - Token usage optimization
126
- - Model response caching
127
-
128
- ### Database
129
- - Primary: PostgreSQL for relational data
130
- - Cache: Redis for session management and real-time features
131
- - Full-text search capabilities
132
- - Database migrations and seeding
133
- - Backup and recovery procedures
134
-
135
- ### Infrastructure
136
- - Docker containerization
137
- - Cloud deployment (AWS/GCP/Azure)
138
- - Auto-scaling capabilities
139
- - Monitoring and logging (structured logging)
140
- - CI/CD pipeline with automated testing
141
-
142
- ## 6. Non-Functional Requirements
143
-
144
- ### Performance
145
- - Page load time < 2 seconds
146
- - API response time < 500ms for 95% of requests
147
- - Support for 1000+ concurrent users
148
- - Efficient handling of large task lists (10,000+ tasks)
149
-
150
- ### Security
151
- - JWT-based authentication with refresh tokens
152
- - Role-based access control (RBAC)
153
- - Data encryption at rest and in transit
154
- - Regular security audits and penetration testing
155
- - GDPR and privacy compliance
156
-
157
- ### Reliability
158
- - 99.9% uptime SLA
159
- - Graceful error handling and recovery
160
- - Data backup every 6 hours with point-in-time recovery
161
- - Disaster recovery plan with RTO < 4 hours
162
-
163
- ### Scalability
164
- - Horizontal scaling for both frontend and backend
165
- - Database read replicas for query optimization
166
- - CDN for static asset delivery
167
- - Microservices architecture for future expansion
168
-
169
- ## 7. User Experience Design
170
-
171
- ### Information Architecture
172
- - Intuitive navigation with breadcrumbs
173
- - Context-aware menus and actions
174
- - Progressive disclosure of complex features
175
- - Consistent design patterns throughout
176
-
177
- ### Interaction Design
178
- - Smooth animations and transitions
179
- - Immediate feedback for user actions
180
- - Undo/redo functionality for critical operations
181
- - Smart defaults and auto-save features
182
-
183
- ### Visual Design
184
- - Modern, clean interface with plenty of whitespace
185
- - Consistent color scheme and typography
186
- - Clear visual hierarchy with proper contrast ratios
187
- - Iconography that supports comprehension
188
-
189
- ## 8. Integration Requirements
190
-
191
- ### Development Tools
192
- - VS Code extension with task panel and quick actions
193
- - Cursor IDE integration with AI task suggestions
194
- - Terminal CLI for command-line workflow
195
- - Browser extension for web-based tools
196
-
197
- ### Third-Party Services
198
- - GitHub/GitLab integration for issue sync
199
- - Slack/Discord notifications
200
- - Calendar integration (Google Calendar, Outlook)
201
- - Time tracking tools (Toggl, Harvest)
202
-
203
- ### APIs and Webhooks
204
- - RESTful API with comprehensive documentation
205
- - GraphQL endpoint for complex queries
206
- - Webhook system for external integrations
207
- - SDK development for major programming languages
208
-
209
- ## 9. Implementation Phases
210
-
211
- ### Phase 1: Core MVP (8-10 weeks)
212
- - Basic task management (CRUD operations)
213
- - Simple AI task generation
214
- - Web interface with essential features
215
- - User authentication and basic permissions
216
-
217
- ### Phase 2: Enhanced Features (6-8 weeks)
218
- - Advanced AI features (complexity analysis, subtask generation)
219
- - Real-time collaboration
220
- - Mobile-responsive design
221
- - Integration with one development tool (VS Code)
222
-
223
- ### Phase 3: Enterprise Features (4-6 weeks)
224
- - Advanced user management and permissions
225
- - API and webhook system
226
- - Performance optimization
227
- - Comprehensive testing and security audit
228
-
229
- ### Phase 4: Ecosystem Expansion (4-6 weeks)
230
- - Additional tool integrations
231
- - Mobile app development
232
- - Advanced analytics and reporting
233
- - Third-party marketplace preparation
234
-
235
- ## 10. Risk Assessment
236
-
237
- ### Technical Risks
238
- - AI model reliability and cost management
239
- - Real-time synchronization complexity
240
- - Database performance with large datasets
241
- - Integration complexity with multiple tools
242
-
243
- ### Business Risks
244
- - User adoption in competitive market
245
- - AI provider dependency
246
- - Data privacy and security concerns
247
- - Feature scope creep and timeline delays
248
-
249
- ### Mitigation Strategies
250
- - Implement robust error handling and fallback systems
251
- - Develop comprehensive testing strategy
252
- - Create detailed documentation and user guides
253
- - Establish clear project scope and change management process
254
-
255
- ## 11. Success Criteria
256
-
257
- ### Development Milestones
258
- - Alpha version with core features completed
259
- - Beta version with selected user group feedback
260
- - Production-ready version with full feature set
261
- - Post-launch iterations based on user feedback
262
-
263
- ### Business Metrics
264
- - User engagement and retention rates
265
- - Task completion and productivity metrics
266
- - Customer satisfaction scores (NPS > 50)
267
- - Revenue targets and subscription growth
268
-
269
- ## 12. Appendices
270
-
271
- ### Glossary
272
- - **PRD**: Product Requirements Document
273
- - **AI**: Artificial Intelligence
274
- - **CRUD**: Create, Read, Update, Delete
275
- - **API**: Application Programming Interface
276
- - **CI/CD**: Continuous Integration/Continuous Deployment
277
-
278
- ### References
279
- - Industry best practices for task management
280
- - AI integration patterns and examples
281
- - Security and compliance requirements
282
- - Performance benchmarking data
283
-
284
- ---
285
-
286
- **Document Control:**
287
- - Version: 1.0
288
- - Last Updated: December 27, 2024
289
- - Next Review: January 15, 2025
290
- - Approved By: Product Owner, Technical Lead`;
291
-
292
- // Initialize filename and load content
293
- useEffect(() => {
294
- const initializeEditor = async () => {
295
- // Set initial filename
296
- if (file?.name) {
297
- setFileName(file.name.replace(/\.(txt|md)$/, '')); // Remove extension for editing
298
- } else if (isNewFile) {
299
- // Generate default filename based on current date
300
- const now = new Date();
301
- const dateStr = now.toISOString().split('T')[0]; // YYYY-MM-DD
302
- setFileName(`prd-${dateStr}`);
303
- }
304
-
305
- // Load content
306
- if (isNewFile) {
307
- setContent(PRD_TEMPLATE);
308
- setLoading(false);
309
- return;
310
- }
311
-
312
- // If content is directly provided (for existing PRDs loaded from API)
313
- if (file.content) {
314
- setContent(file.content);
315
- setLoading(false);
316
- return;
317
- }
318
-
319
- // Fallback to loading from file path (legacy support)
320
- try {
321
- setLoading(true);
322
-
323
- const response = await api.readFile(file.projectName, file.path);
324
-
325
- if (!response.ok) {
326
- throw new Error(`Failed to load file: ${response.status} ${response.statusText}`);
327
- }
328
-
329
- const data = await response.json();
330
- setContent(data.content || PRD_TEMPLATE);
331
- } catch (error) {
332
- console.error('Error loading PRD file:', error);
333
- setContent(`# Error Loading PRD\n\nError: ${error.message}\n\nFile: ${file?.name || 'New PRD'}\nPath: ${file?.path || 'Not saved yet'}\n\n${PRD_TEMPLATE}`);
334
- } finally {
335
- setLoading(false);
336
- }
337
- };
338
-
339
- initializeEditor();
340
- }, [file, projectPath, isNewFile]);
341
-
342
- // Fetch existing PRDs to check for conflicts
343
- useEffect(() => {
344
- const fetchExistingPRDs = async () => {
345
- if (!project?.name) {
346
- console.log('No project name available:', project);
347
- return;
348
- }
349
-
350
- try {
351
- console.log('Fetching PRDs for project:', project.name);
352
- const response = await api.get(`/taskmaster/prd/${encodeURIComponent(project.name)}`);
353
- if (response.ok) {
354
- const data = await response.json();
355
- console.log('Fetched existing PRDs:', data.prds);
356
- setExistingPRDs(data.prds || []);
357
- } else {
358
- console.log('Failed to fetch PRDs:', response.status, response.statusText);
359
- }
360
- } catch (error) {
361
- console.error('Error fetching existing PRDs:', error);
362
- }
363
- };
364
-
365
- fetchExistingPRDs();
366
- }, [project?.name]);
367
-
368
- const handleSave = async () => {
369
- if (!content.trim()) {
370
- alert('Please add content before saving.');
371
- return;
372
- }
373
-
374
- if (!fileName.trim()) {
375
- alert('Please provide a filename for the PRD.');
376
- return;
377
- }
378
-
379
- // Check if file already exists
380
- const fullFileName = fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`;
381
- const existingFile = existingPRDs.find(prd => prd.name === fullFileName);
382
-
383
- console.log('Save check:', {
384
- fullFileName,
385
- existingPRDs,
386
- existingFile,
387
- isExisting: file?.isExisting,
388
- fileObject: file,
389
- shouldShowModal: existingFile && !file?.isExisting
390
- });
391
-
392
- if (existingFile && !file?.isExisting) {
393
- console.log('Showing overwrite confirmation modal');
394
- // Show confirmation modal for overwrite
395
- setShowOverwriteConfirm(true);
396
- return;
397
- }
398
-
399
- await performSave();
400
- };
401
-
402
- const performSave = async () => {
403
- setSaving(true);
404
- try {
405
- // Ensure filename has .txt extension
406
- const fullFileName = fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`;
407
-
408
- const response = await authenticatedFetch(`/api/taskmaster/prd/${encodeURIComponent(project?.name)}`, {
409
- method: 'POST',
410
- body: JSON.stringify({
411
- fileName: fullFileName,
412
- content
413
- })
414
- });
415
-
416
- if (!response.ok) {
417
- const errorData = await response.json();
418
- throw new Error(errorData.message || `Save failed: ${response.status}`);
419
- }
420
-
421
- // Show success feedback
422
- setSaveSuccess(true);
423
- setTimeout(() => setSaveSuccess(false), 2000);
424
-
425
- // Update existing PRDs list
426
- const response2 = await api.get(`/taskmaster/prd/${encodeURIComponent(project.name)}`);
427
- if (response2.ok) {
428
- const data = await response2.json();
429
- setExistingPRDs(data.prds || []);
430
- }
431
-
432
- // Call the onSave callback if provided (for UI updates)
433
- if (onSave) {
434
- await onSave();
435
- }
436
-
437
- } catch (error) {
438
- console.error('Error saving PRD:', error);
439
- alert(`Error saving PRD: ${error.message}`);
440
- } finally {
441
- setSaving(false);
442
- }
443
- };
444
-
445
- const handleDownload = () => {
446
- const blob = new Blob([content], { type: 'text/markdown' });
447
- const url = URL.createObjectURL(blob);
448
- const a = document.createElement('a');
449
- a.href = url;
450
- const downloadFileName = fileName ? `${fileName}.txt` : 'prd.txt';
451
- a.download = downloadFileName;
452
- document.body.appendChild(a);
453
- a.click();
454
- document.body.removeChild(a);
455
- URL.revokeObjectURL(url);
456
- };
457
-
458
- const handleGenerateTasks = async () => {
459
- if (!content.trim()) {
460
- alert('Please add content to the PRD before generating tasks.');
461
- return;
462
- }
463
-
464
- // Show AI-first modal instead of simple confirm
465
- setShowGenerateModal(true);
466
- };
467
-
468
-
469
- const toggleFullscreen = () => {
470
- setIsFullscreen(!isFullscreen);
471
- };
472
-
473
- // Handle keyboard shortcuts
474
- useEffect(() => {
475
- const handleKeyDown = (e) => {
476
- if (e.ctrlKey || e.metaKey) {
477
- if (e.key === 's') {
478
- e.preventDefault();
479
- handleSave();
480
- } else if (e.key === 'Escape') {
481
- e.preventDefault();
482
- onClose();
483
- }
484
- }
485
- };
486
-
487
- document.addEventListener('keydown', handleKeyDown);
488
- return () => document.removeEventListener('keydown', handleKeyDown);
489
- }, [content]);
490
-
491
- // Simple markdown to HTML converter for preview
492
- const renderMarkdown = (markdown) => {
493
- return markdown
494
- .replace(/^### (.*$)/gim, '<h3>$1</h3>')
495
- .replace(/^## (.*$)/gim, '<h2>$1</h2>')
496
- .replace(/^# (.*$)/gim, '<h1>$1</h1>')
497
- .replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
498
- .replace(/\*(.*)\*/gim, '<em>$1</em>')
499
- .replace(/^\- (.*$)/gim, '<li>$1</li>')
500
- .replace(/(<li>.*<\/li>)/gims, '<ul>$1</ul>')
501
- .replace(/\n\n/gim, '</p><p>')
502
- .replace(/^(?!<[h|u|l])(.*$)/gim, '<p>$1</p>')
503
- .replace(/<\/ul>\s*<ul>/gim, '');
504
- };
505
-
506
- if (loading) {
507
- return (
508
- <div className="fixed inset-0 z-[200] md:bg-black/50 md:flex md:items-center md:justify-center">
509
- <div className="w-full h-full md:rounded-lg md:w-auto md:h-auto p-8 flex items-center justify-center bg-white dark:bg-gray-900">
510
- <div className="flex items-center gap-3">
511
- <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
512
- <span className="text-gray-900 dark:text-white">Loading PRD...</span>
513
- </div>
514
- </div>
515
- </div>
516
- );
517
- }
518
-
519
- return (
520
- <div className={`fixed inset-0 z-[200] ${
521
- 'md:bg-black/50 md:flex md:items-center md:justify-center md:p-4'
522
- } ${isFullscreen ? 'md:p-0' : ''}`}>
523
- <div className={cn(
524
- 'bg-white dark:bg-gray-900 shadow-2xl flex flex-col',
525
- 'w-full h-full md:rounded-lg md:shadow-2xl',
526
- isFullscreen
527
- ? 'md:w-full md:h-full md:rounded-none'
528
- : 'md:w-full md:max-w-6xl md:h-[85vh] md:max-h-[85vh]'
529
- )}>
530
- {/* Header */}
531
- <div className="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0 min-w-0">
532
- <div className="flex items-center gap-3 min-w-0 flex-1">
533
- <div className="w-8 h-8 bg-purple-600 rounded flex items-center justify-center flex-shrink-0">
534
- <FileText className="w-4 h-4 text-white" />
535
- </div>
536
- <div className="min-w-0 flex-1">
537
- {/* Mobile: Stack filename and tags vertically for more space */}
538
- <div className="flex flex-col sm:flex-row sm:items-center gap-2 min-w-0">
539
- {/* Filename input row - full width on mobile */}
540
- <div className="flex items-center gap-1 min-w-0 flex-1">
541
- <div className="flex items-center min-w-0 flex-1 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-md px-3 py-2 focus-within:ring-2 focus-within:ring-purple-500 focus-within:border-purple-500 dark:focus-within:ring-purple-400 dark:focus-within:border-purple-400">
542
- <input
543
- type="text"
544
- value={fileName}
545
- onChange={(e) => {
546
- // Remove invalid filename characters
547
- const sanitizedValue = e.target.value.replace(/[<>:"/\\|?*]/g, '');
548
- setFileName(sanitizedValue);
549
- }}
550
- className="font-medium text-gray-900 dark:text-white bg-transparent border-none outline-none min-w-0 flex-1 text-base sm:text-sm placeholder-gray-400 dark:placeholder-gray-500"
551
- placeholder="Enter PRD filename"
552
- maxLength={100}
553
- />
554
- <span className="text-sm sm:text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap ml-1">.txt</span>
555
- </div>
556
- <button
557
- onClick={() => document.querySelector('input[placeholder="Enter PRD filename"]')?.focus()}
558
- className="p-1 text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
559
- title="Click to edit filename"
560
- >
561
- <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
562
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
563
- </svg>
564
- </button>
565
- </div>
566
-
567
- {/* Tags row - moves to second line on mobile for more filename space */}
568
- <div className="flex items-center gap-2 flex-shrink-0">
569
- <span className="text-xs bg-purple-100 dark:bg-purple-900 text-purple-600 dark:text-purple-300 px-2 py-1 rounded whitespace-nowrap">
570
- 📋 PRD
571
- </span>
572
- {isNewFile && (
573
- <span className="text-xs bg-green-100 dark:bg-green-900 text-green-600 dark:text-green-300 px-2 py-1 rounded whitespace-nowrap">
574
- ✨ New
575
- </span>
576
- )}
577
- </div>
578
- </div>
579
-
580
- {/* Description - smaller on mobile */}
581
- <p className="text-xs sm:text-sm text-gray-500 dark:text-gray-400 truncate mt-1">
582
- Product Requirements Document
583
- </p>
584
- </div>
585
- </div>
586
-
587
- <div className="flex items-center gap-1 md:gap-2 flex-shrink-0">
588
- <button
589
- onClick={() => setPreviewMode(!previewMode)}
590
- className={cn(
591
- 'p-2 md:p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800',
592
- 'min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center',
593
- previewMode
594
- ? 'text-purple-600 dark:text-purple-400 bg-purple-50 dark:bg-purple-900'
595
- : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'
596
- )}
597
- title={previewMode ? 'Switch to edit mode' : 'Preview markdown'}
598
- >
599
- <Eye className="w-5 h-5 md:w-4 md:h-4" />
600
- </button>
601
-
602
- <button
603
- onClick={() => setWordWrap(!wordWrap)}
604
- className={cn(
605
- 'p-2 md:p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800',
606
- 'min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center',
607
- wordWrap
608
- ? 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-900'
609
- : 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white'
610
- )}
611
- title={wordWrap ? 'Disable word wrap' : 'Enable word wrap'}
612
- >
613
- <span className="text-sm md:text-xs font-mono font-bold">↵</span>
614
- </button>
615
-
616
- <button
617
- onClick={() => setIsDarkMode(!isDarkMode)}
618
- className="p-2 md:p-2 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-800 min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center"
619
- title="Toggle theme"
620
- >
621
- <span className="text-lg md:text-base">{isDarkMode ? '☀️' : '🌙'}</span>
622
- </button>
623
-
624
- <button
625
- onClick={handleDownload}
626
- className="p-2 md:p-2 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-800 min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center"
627
- title="Download PRD"
628
- >
629
- <Download className="w-5 h-5 md:w-4 md:h-4" />
630
- </button>
631
-
632
- <button
633
- onClick={handleGenerateTasks}
634
- disabled={!content.trim()}
635
- className={cn(
636
- 'px-3 py-2 rounded-md disabled:opacity-50 flex items-center gap-2 transition-colors text-sm font-medium',
637
- 'bg-purple-600 hover:bg-purple-700 text-white',
638
- 'min-h-[44px] md:min-h-0'
639
- )}
640
- title="Generate tasks from PRD content"
641
- >
642
- <Sparkles className="w-4 h-4" />
643
- <span className="hidden md:inline">Generate Tasks</span>
644
- </button>
645
-
646
- <button
647
- onClick={handleSave}
648
- disabled={saving}
649
- className={cn(
650
- 'px-3 py-2 text-white rounded-md disabled:opacity-50 flex items-center gap-2 transition-colors',
651
- 'min-h-[44px] md:min-h-0',
652
- saveSuccess
653
- ? 'bg-green-600 hover:bg-green-700'
654
- : 'bg-purple-600 hover:bg-purple-700'
655
- )}
656
- >
657
- {saveSuccess ? (
658
- <>
659
- <svg className="w-5 h-5 md:w-4 md:h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
660
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
661
- </svg>
662
- <span className="hidden sm:inline">Saved!</span>
663
- </>
664
- ) : (
665
- <>
666
- <Save className="w-5 h-5 md:w-4 md:h-4" />
667
- <span className="hidden sm:inline">{saving ? 'Saving...' : 'Save PRD'}</span>
668
- </>
669
- )}
670
- </button>
671
-
672
- <button
673
- onClick={toggleFullscreen}
674
- className="hidden md:flex p-2 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-800 items-center justify-center"
675
- title={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
676
- >
677
- {isFullscreen ? <Minimize2 className="w-4 h-4" /> : <Maximize2 className="w-4 h-4" />}
678
- </button>
679
-
680
- <button
681
- onClick={onClose}
682
- className="p-2 md:p-2 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-800 min-w-[44px] min-h-[44px] md:min-w-0 md:min-h-0 flex items-center justify-center"
683
- title="Close"
684
- >
685
- <X className="w-6 h-6 md:w-4 md:h-4" />
686
- </button>
687
- </div>
688
- </div>
689
-
690
- {/* Editor/Preview Content */}
691
- <div className="flex-1 overflow-hidden">
692
- {previewMode ? (
693
- <div className="h-full overflow-y-auto p-6 prose prose-gray dark:prose-invert max-w-none">
694
- <div
695
- className="markdown-preview"
696
- dangerouslySetInnerHTML={{ __html: renderMarkdown(content) }}
697
- />
698
- </div>
699
- ) : (
700
- <CodeMirror
701
- ref={editorRef}
702
- value={content}
703
- onChange={setContent}
704
- extensions={[
705
- markdown(),
706
- ...(wordWrap ? [EditorView.lineWrapping] : [])
707
- ]}
708
- theme={isDarkMode ? oneDark : undefined}
709
- height="100%"
710
- style={{
711
- fontSize: '14px',
712
- height: '100%',
713
- }}
714
- basicSetup={{
715
- lineNumbers: true,
716
- foldGutter: true,
717
- dropCursor: false,
718
- allowMultipleSelections: false,
719
- indentOnInput: true,
720
- bracketMatching: true,
721
- closeBrackets: true,
722
- autocompletion: true,
723
- highlightSelectionMatches: true,
724
- searchKeymap: true,
725
- }}
726
- />
727
- )}
728
- </div>
729
-
730
- {/* Footer */}
731
- <div className="flex items-center justify-between p-3 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 flex-shrink-0">
732
- <div className="flex items-center gap-4 text-sm text-gray-600 dark:text-gray-400">
733
- <span>Lines: {content.split('\n').length}</span>
734
- <span>Characters: {content.length}</span>
735
- <span>Words: {content.split(/\s+/).filter(word => word.length > 0).length}</span>
736
- <span>Format: Markdown</span>
737
- </div>
738
-
739
- <div className="text-sm text-gray-500 dark:text-gray-400">
740
- Press Ctrl+S to save • Esc to close
741
- </div>
742
- </div>
743
- </div>
744
-
745
- {/* Generate Tasks Modal */}
746
- {showGenerateModal && (
747
- <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4">
748
- <div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md border border-gray-200 dark:border-gray-700">
749
- {/* Header */}
750
- <div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
751
- <div className="flex items-center gap-3">
752
- <div className="w-8 h-8 bg-purple-100 dark:bg-purple-900/50 rounded-lg flex items-center justify-center">
753
- <Sparkles className="w-4 h-4 text-purple-600 dark:text-purple-400" />
754
- </div>
755
- <h3 className="text-lg font-semibold text-gray-900 dark:text-white">Generate Tasks from PRD</h3>
756
- </div>
757
- <button
758
- onClick={() => setShowGenerateModal(false)}
759
- 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-700"
760
- >
761
- <X className="w-5 h-5" />
762
- </button>
763
- </div>
764
-
765
- {/* Content */}
766
- <div className="p-6 space-y-4">
767
- {/* AI-First Approach */}
768
- <div className="bg-purple-50 dark:bg-purple-900/20 rounded-lg p-4 border border-purple-200 dark:border-purple-800">
769
- <div className="flex items-start gap-3">
770
- <div className="w-8 h-8 bg-purple-100 dark:bg-purple-900/50 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
771
- <Sparkles className="w-4 h-4 text-purple-600 dark:text-purple-400" />
772
- </div>
773
- <div className="flex-1">
774
- <h4 className="font-semibold text-purple-900 dark:text-purple-100 mb-2">
775
- 💡 Pro Tip: Ask Claude Code Directly!
776
- </h4>
777
- <p className="text-sm text-purple-800 dark:text-purple-200 mb-3">
778
- You can simply ask Claude Code in the chat to parse your PRD and generate tasks.
779
- The AI assistant will automatically save your PRD and create detailed tasks with implementation details.
780
- </p>
781
-
782
- <div className="bg-white dark:bg-gray-800 rounded border border-purple-200 dark:border-purple-700 p-3 mb-3">
783
- <p className="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">💬 Example:</p>
784
- <p className="text-xs text-gray-900 dark:text-white font-mono">
785
- "I've just initialized a new project with Claude Task Master. I have a PRD at .taskmaster/docs/{fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`}. Can you help me parse it and set up the initial tasks?"
786
- </p>
787
- </div>
788
-
789
- <p className="text-xs text-purple-700 dark:text-purple-300">
790
- <strong>This will:</strong> Save your PRD, analyze its content, and generate structured tasks with subtasks, dependencies, and implementation details.
791
- </p>
792
- </div>
793
- </div>
794
- </div>
795
-
796
- {/* Learn More Link */}
797
- <div className="text-center pt-4 border-t border-gray-200 dark:border-gray-700">
798
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
799
- For more examples and advanced usage patterns:
800
- </p>
801
- <a
802
- href="https://github.com/eyaltoledano/claude-task-master/blob/main/docs/examples.md"
803
- target="_blank"
804
- rel="noopener noreferrer"
805
- className="inline-block text-sm text-purple-600 dark:text-purple-400 hover:text-purple-700 dark:hover:text-purple-300 underline font-medium"
806
- >
807
- View TaskMaster Documentation →
808
- </a>
809
- </div>
810
-
811
- {/* Footer */}
812
- <div className="pt-4">
813
- <button
814
- onClick={() => setShowGenerateModal(false)}
815
- className="w-full 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-lg hover:bg-gray-50 dark:hover:bg-gray-600 transition-colors"
816
- >
817
- Got it, I'll ask Claude Code directly
818
- </button>
819
- </div>
820
- </div>
821
- </div>
822
- </div>
823
- )}
824
-
825
- {/* Overwrite Confirmation Modal */}
826
- {showOverwriteConfirm && (
827
- <div className="fixed inset-0 z-[300] flex items-center justify-center p-4">
828
- <div className="fixed inset-0 bg-black bg-opacity-50" onClick={() => setShowOverwriteConfirm(false)} />
829
- <div className="relative bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full border border-gray-200 dark:border-gray-700">
830
- <div className="p-6">
831
- <div className="flex items-center mb-4">
832
- <div className="p-2 rounded-full mr-3 bg-yellow-100 dark:bg-yellow-900">
833
- <AlertTriangle className="w-5 h-5 text-yellow-600 dark:text-yellow-400" />
834
- </div>
835
- <h3 className="text-lg font-semibold text-gray-900 dark:text-white">
836
- File Already Exists
837
- </h3>
838
- </div>
839
-
840
- <p className="text-sm text-gray-600 dark:text-gray-400 mb-6">
841
- A PRD file named "{fileName.endsWith('.txt') || fileName.endsWith('.md') ? fileName : `${fileName}.txt`}" already exists.
842
- Do you want to overwrite it with the current content?
843
- </p>
844
-
845
- <div className="flex justify-end space-x-3">
846
- <button
847
- onClick={() => setShowOverwriteConfirm(false)}
848
- className="px-4 py-2 text-sm 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"
849
- >
850
- Cancel
851
- </button>
852
- <button
853
- onClick={async () => {
854
- setShowOverwriteConfirm(false);
855
- await performSave();
856
- }}
857
- className="px-4 py-2 text-sm text-white bg-yellow-600 hover:bg-yellow-700 rounded-md flex items-center space-x-2 transition-colors"
858
- >
859
- <Save className="w-4 h-4" />
860
- <span>Overwrite</span>
861
- </button>
862
- </div>
863
- </div>
864
- </div>
865
- </div>
866
- )}
867
- </div>
868
- );
869
- };
870
-
871
- export default PRDEditor;