@siteboon/claude-code-ui 1.8.2

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 (106) hide show
  1. package/.env.example +12 -0
  2. package/.nvmrc +1 -0
  3. package/LICENSE +675 -0
  4. package/README.md +275 -0
  5. package/index.html +48 -0
  6. package/package.json +84 -0
  7. package/postcss.config.js +6 -0
  8. package/public/convert-icons.md +53 -0
  9. package/public/favicon.png +0 -0
  10. package/public/favicon.svg +9 -0
  11. package/public/generate-icons.js +49 -0
  12. package/public/icons/claude-ai-icon.svg +1 -0
  13. package/public/icons/cursor.svg +1 -0
  14. package/public/icons/generate-icons.md +19 -0
  15. package/public/icons/icon-128x128.png +0 -0
  16. package/public/icons/icon-128x128.svg +12 -0
  17. package/public/icons/icon-144x144.png +0 -0
  18. package/public/icons/icon-144x144.svg +12 -0
  19. package/public/icons/icon-152x152.png +0 -0
  20. package/public/icons/icon-152x152.svg +12 -0
  21. package/public/icons/icon-192x192.png +0 -0
  22. package/public/icons/icon-192x192.svg +12 -0
  23. package/public/icons/icon-384x384.png +0 -0
  24. package/public/icons/icon-384x384.svg +12 -0
  25. package/public/icons/icon-512x512.png +0 -0
  26. package/public/icons/icon-512x512.svg +12 -0
  27. package/public/icons/icon-72x72.png +0 -0
  28. package/public/icons/icon-72x72.svg +12 -0
  29. package/public/icons/icon-96x96.png +0 -0
  30. package/public/icons/icon-96x96.svg +12 -0
  31. package/public/icons/icon-template.svg +12 -0
  32. package/public/logo.svg +9 -0
  33. package/public/manifest.json +61 -0
  34. package/public/screenshots/cli-selection.png +0 -0
  35. package/public/screenshots/desktop-main.png +0 -0
  36. package/public/screenshots/mobile-chat.png +0 -0
  37. package/public/screenshots/tools-modal.png +0 -0
  38. package/public/sw.js +49 -0
  39. package/server/claude-cli.js +391 -0
  40. package/server/cursor-cli.js +250 -0
  41. package/server/database/db.js +86 -0
  42. package/server/database/init.sql +16 -0
  43. package/server/index.js +1167 -0
  44. package/server/middleware/auth.js +80 -0
  45. package/server/projects.js +1063 -0
  46. package/server/routes/auth.js +135 -0
  47. package/server/routes/cursor.js +794 -0
  48. package/server/routes/git.js +823 -0
  49. package/server/routes/mcp-utils.js +48 -0
  50. package/server/routes/mcp.js +552 -0
  51. package/server/routes/taskmaster.js +1971 -0
  52. package/server/utils/mcp-detector.js +198 -0
  53. package/server/utils/taskmaster-websocket.js +129 -0
  54. package/src/App.jsx +751 -0
  55. package/src/components/ChatInterface.jsx +3485 -0
  56. package/src/components/ClaudeLogo.jsx +11 -0
  57. package/src/components/ClaudeStatus.jsx +107 -0
  58. package/src/components/CodeEditor.jsx +422 -0
  59. package/src/components/CreateTaskModal.jsx +88 -0
  60. package/src/components/CursorLogo.jsx +9 -0
  61. package/src/components/DarkModeToggle.jsx +35 -0
  62. package/src/components/DiffViewer.jsx +41 -0
  63. package/src/components/ErrorBoundary.jsx +73 -0
  64. package/src/components/FileTree.jsx +480 -0
  65. package/src/components/GitPanel.jsx +1283 -0
  66. package/src/components/ImageViewer.jsx +54 -0
  67. package/src/components/LoginForm.jsx +110 -0
  68. package/src/components/MainContent.jsx +577 -0
  69. package/src/components/MicButton.jsx +272 -0
  70. package/src/components/MobileNav.jsx +88 -0
  71. package/src/components/NextTaskBanner.jsx +695 -0
  72. package/src/components/PRDEditor.jsx +871 -0
  73. package/src/components/ProtectedRoute.jsx +44 -0
  74. package/src/components/QuickSettingsPanel.jsx +262 -0
  75. package/src/components/Settings.jsx +2023 -0
  76. package/src/components/SetupForm.jsx +135 -0
  77. package/src/components/Shell.jsx +663 -0
  78. package/src/components/Sidebar.jsx +1665 -0
  79. package/src/components/StandaloneShell.jsx +106 -0
  80. package/src/components/TaskCard.jsx +210 -0
  81. package/src/components/TaskDetail.jsx +406 -0
  82. package/src/components/TaskIndicator.jsx +108 -0
  83. package/src/components/TaskList.jsx +1054 -0
  84. package/src/components/TaskMasterSetupWizard.jsx +603 -0
  85. package/src/components/TaskMasterStatus.jsx +86 -0
  86. package/src/components/TodoList.jsx +91 -0
  87. package/src/components/Tooltip.jsx +91 -0
  88. package/src/components/ui/badge.jsx +31 -0
  89. package/src/components/ui/button.jsx +46 -0
  90. package/src/components/ui/input.jsx +19 -0
  91. package/src/components/ui/scroll-area.jsx +23 -0
  92. package/src/contexts/AuthContext.jsx +158 -0
  93. package/src/contexts/TaskMasterContext.jsx +324 -0
  94. package/src/contexts/TasksSettingsContext.jsx +95 -0
  95. package/src/contexts/ThemeContext.jsx +94 -0
  96. package/src/contexts/WebSocketContext.jsx +29 -0
  97. package/src/hooks/useAudioRecorder.js +109 -0
  98. package/src/hooks/useVersionCheck.js +39 -0
  99. package/src/index.css +822 -0
  100. package/src/lib/utils.js +6 -0
  101. package/src/main.jsx +10 -0
  102. package/src/utils/api.js +141 -0
  103. package/src/utils/websocket.js +109 -0
  104. package/src/utils/whisper.js +37 -0
  105. package/tailwind.config.js +63 -0
  106. package/vite.config.js +29 -0
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import { Badge } from './ui/badge';
3
+ import { CheckCircle2, Clock, Circle } from 'lucide-react';
4
+
5
+ const TodoList = ({ todos, isResult = false }) => {
6
+ if (!todos || !Array.isArray(todos)) {
7
+ return null;
8
+ }
9
+
10
+ const getStatusIcon = (status) => {
11
+ switch (status) {
12
+ case 'completed':
13
+ return <CheckCircle2 className="w-4 h-4 text-green-500 dark:text-green-400" />;
14
+ case 'in_progress':
15
+ return <Clock className="w-4 h-4 text-blue-500 dark:text-blue-400" />;
16
+ case 'pending':
17
+ default:
18
+ return <Circle className="w-4 h-4 text-gray-400 dark:text-gray-500" />;
19
+ }
20
+ };
21
+
22
+ const getStatusColor = (status) => {
23
+ switch (status) {
24
+ case 'completed':
25
+ return 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border-green-200 dark:border-green-800';
26
+ case 'in_progress':
27
+ return 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border-blue-200 dark:border-blue-800';
28
+ case 'pending':
29
+ default:
30
+ return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700';
31
+ }
32
+ };
33
+
34
+ const getPriorityColor = (priority) => {
35
+ switch (priority) {
36
+ case 'high':
37
+ return 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 border-red-200 dark:border-red-800';
38
+ case 'medium':
39
+ return 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800';
40
+ case 'low':
41
+ default:
42
+ return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700';
43
+ }
44
+ };
45
+
46
+ return (
47
+ <div className="space-y-3">
48
+ {isResult && (
49
+ <div className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
50
+ Todo List ({todos.length} {todos.length === 1 ? 'item' : 'items'})
51
+ </div>
52
+ )}
53
+
54
+ {todos.map((todo, index) => (
55
+ <div
56
+ key={todo.id || `todo-${index}`}
57
+ className="flex items-start gap-3 p-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm hover:shadow-md dark:shadow-gray-900/50 transition-shadow"
58
+ >
59
+ <div className="flex-shrink-0 mt-0.5">
60
+ {getStatusIcon(todo.status)}
61
+ </div>
62
+
63
+ <div className="flex-1 min-w-0">
64
+ <div className="flex items-start justify-between gap-2 mb-2">
65
+ <p className={`text-sm font-medium ${todo.status === 'completed' ? 'line-through text-gray-500 dark:text-gray-400' : 'text-gray-900 dark:text-gray-100'}`}>
66
+ {todo.content}
67
+ </p>
68
+
69
+ <div className="flex gap-1 flex-shrink-0">
70
+ <Badge
71
+ variant="outline"
72
+ className={`text-xs px-2 py-0.5 ${getPriorityColor(todo.priority)}`}
73
+ >
74
+ {todo.priority}
75
+ </Badge>
76
+ <Badge
77
+ variant="outline"
78
+ className={`text-xs px-2 py-0.5 ${getStatusColor(todo.status)}`}
79
+ >
80
+ {todo.status.replace('_', ' ')}
81
+ </Badge>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ ))}
87
+ </div>
88
+ );
89
+ };
90
+
91
+ export default TodoList;
@@ -0,0 +1,91 @@
1
+ import React, { useState } from 'react';
2
+ import { cn } from '../lib/utils';
3
+
4
+ const Tooltip = ({
5
+ children,
6
+ content,
7
+ position = 'top',
8
+ className = '',
9
+ delay = 500
10
+ }) => {
11
+ const [isVisible, setIsVisible] = useState(false);
12
+ const [timeoutId, setTimeoutId] = useState(null);
13
+
14
+ const handleMouseEnter = () => {
15
+ const id = setTimeout(() => {
16
+ setIsVisible(true);
17
+ }, delay);
18
+ setTimeoutId(id);
19
+ };
20
+
21
+ const handleMouseLeave = () => {
22
+ if (timeoutId) {
23
+ clearTimeout(timeoutId);
24
+ setTimeoutId(null);
25
+ }
26
+ setIsVisible(false);
27
+ };
28
+
29
+ const getPositionClasses = () => {
30
+ switch (position) {
31
+ case 'top':
32
+ return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
33
+ case 'bottom':
34
+ return 'top-full left-1/2 transform -translate-x-1/2 mt-2';
35
+ case 'left':
36
+ return 'right-full top-1/2 transform -translate-y-1/2 mr-2';
37
+ case 'right':
38
+ return 'left-full top-1/2 transform -translate-y-1/2 ml-2';
39
+ default:
40
+ return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
41
+ }
42
+ };
43
+
44
+ const getArrowClasses = () => {
45
+ switch (position) {
46
+ case 'top':
47
+ return 'top-full left-1/2 transform -translate-x-1/2 border-t-gray-900 dark:border-t-gray-100';
48
+ case 'bottom':
49
+ return 'bottom-full left-1/2 transform -translate-x-1/2 border-b-gray-900 dark:border-b-gray-100';
50
+ case 'left':
51
+ return 'left-full top-1/2 transform -translate-y-1/2 border-l-gray-900 dark:border-l-gray-100';
52
+ case 'right':
53
+ return 'right-full top-1/2 transform -translate-y-1/2 border-r-gray-900 dark:border-r-gray-100';
54
+ default:
55
+ return 'top-full left-1/2 transform -translate-x-1/2 border-t-gray-900 dark:border-t-gray-100';
56
+ }
57
+ };
58
+
59
+ if (!content) {
60
+ return children;
61
+ }
62
+
63
+ return (
64
+ <div
65
+ className="relative inline-block"
66
+ onMouseEnter={handleMouseEnter}
67
+ onMouseLeave={handleMouseLeave}
68
+ >
69
+ {children}
70
+
71
+ {isVisible && (
72
+ <div className={cn(
73
+ 'absolute z-50 px-2 py-1 text-xs font-medium text-white bg-gray-900 dark:bg-gray-100 dark:text-gray-900 rounded shadow-lg whitespace-nowrap pointer-events-none',
74
+ 'animate-in fade-in-0 zoom-in-95 duration-200',
75
+ getPositionClasses(),
76
+ className
77
+ )}>
78
+ {content}
79
+
80
+ {/* Arrow */}
81
+ <div className={cn(
82
+ 'absolute w-0 h-0 border-4 border-transparent',
83
+ getArrowClasses()
84
+ )} />
85
+ </div>
86
+ )}
87
+ </div>
88
+ );
89
+ };
90
+
91
+ export default Tooltip;
@@ -0,0 +1,31 @@
1
+ import * as React from "react"
2
+ import { cva } from "class-variance-authority"
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const badgeVariants = cva(
6
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default:
11
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
12
+ secondary:
13
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
14
+ destructive:
15
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
16
+ outline: "text-foreground",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: "default",
21
+ },
22
+ }
23
+ )
24
+
25
+ function Badge({ className, variant, ...props }) {
26
+ return (
27
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
28
+ )
29
+ }
30
+
31
+ export { Badge, badgeVariants }
@@ -0,0 +1,46 @@
1
+ import * as React from "react"
2
+ import { cva } from "class-variance-authority"
3
+ import { cn } from "../../lib/utils"
4
+
5
+ const buttonVariants = cva(
6
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default:
11
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
12
+ destructive:
13
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
14
+ outline:
15
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
16
+ secondary:
17
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
18
+ ghost: "hover:bg-accent hover:text-accent-foreground",
19
+ link: "text-primary underline-offset-4 hover:underline",
20
+ },
21
+ size: {
22
+ default: "h-9 px-4 py-2",
23
+ sm: "h-8 rounded-md px-3 text-xs",
24
+ lg: "h-10 rounded-md px-8",
25
+ icon: "h-9 w-9",
26
+ },
27
+ },
28
+ defaultVariants: {
29
+ variant: "default",
30
+ size: "default",
31
+ },
32
+ }
33
+ )
34
+
35
+ const Button = React.forwardRef(({ className, variant, size, ...props }, ref) => {
36
+ return (
37
+ <button
38
+ className={cn(buttonVariants({ variant, size, className }))}
39
+ ref={ref}
40
+ {...props}
41
+ />
42
+ )
43
+ })
44
+ Button.displayName = "Button"
45
+
46
+ export { Button, buttonVariants }
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ const Input = React.forwardRef(({ className, type, ...props }, ref) => {
5
+ return (
6
+ <input
7
+ type={type}
8
+ className={cn(
9
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
10
+ className
11
+ )}
12
+ ref={ref}
13
+ {...props}
14
+ />
15
+ )
16
+ })
17
+ Input.displayName = "Input"
18
+
19
+ export { Input }
@@ -0,0 +1,23 @@
1
+ import * as React from "react"
2
+ import { cn } from "../../lib/utils"
3
+
4
+ const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => (
5
+ <div
6
+ ref={ref}
7
+ className={cn("relative overflow-hidden", className)}
8
+ {...props}
9
+ >
10
+ <div
11
+ className="h-full w-full rounded-[inherit] overflow-auto"
12
+ style={{
13
+ WebkitOverflowScrolling: 'touch',
14
+ touchAction: 'pan-y'
15
+ }}
16
+ >
17
+ {children}
18
+ </div>
19
+ </div>
20
+ ))
21
+ ScrollArea.displayName = "ScrollArea"
22
+
23
+ export { ScrollArea }
@@ -0,0 +1,158 @@
1
+ import React, { createContext, useContext, useEffect, useState } from 'react';
2
+ import { api } from '../utils/api';
3
+
4
+ const AuthContext = createContext({
5
+ user: null,
6
+ token: null,
7
+ login: () => {},
8
+ register: () => {},
9
+ logout: () => {},
10
+ isLoading: true,
11
+ needsSetup: false,
12
+ error: null
13
+ });
14
+
15
+ export const useAuth = () => {
16
+ const context = useContext(AuthContext);
17
+ if (!context) {
18
+ throw new Error('useAuth must be used within an AuthProvider');
19
+ }
20
+ return context;
21
+ };
22
+
23
+ export const AuthProvider = ({ children }) => {
24
+ const [user, setUser] = useState(null);
25
+ const [token, setToken] = useState(localStorage.getItem('auth-token'));
26
+ const [isLoading, setIsLoading] = useState(true);
27
+ const [needsSetup, setNeedsSetup] = useState(false);
28
+ const [error, setError] = useState(null);
29
+
30
+ // Check authentication status on mount
31
+ useEffect(() => {
32
+ checkAuthStatus();
33
+ }, []);
34
+
35
+ const checkAuthStatus = async () => {
36
+ try {
37
+ setIsLoading(true);
38
+ setError(null);
39
+
40
+ // Check if system needs setup
41
+ const statusResponse = await api.auth.status();
42
+ const statusData = await statusResponse.json();
43
+
44
+ if (statusData.needsSetup) {
45
+ setNeedsSetup(true);
46
+ setIsLoading(false);
47
+ return;
48
+ }
49
+
50
+ // If we have a token, verify it
51
+ if (token) {
52
+ try {
53
+ const userResponse = await api.auth.user();
54
+
55
+ if (userResponse.ok) {
56
+ const userData = await userResponse.json();
57
+ setUser(userData.user);
58
+ setNeedsSetup(false);
59
+ } else {
60
+ // Token is invalid
61
+ localStorage.removeItem('auth-token');
62
+ setToken(null);
63
+ setUser(null);
64
+ }
65
+ } catch (error) {
66
+ console.error('Token verification failed:', error);
67
+ localStorage.removeItem('auth-token');
68
+ setToken(null);
69
+ setUser(null);
70
+ }
71
+ }
72
+ } catch (error) {
73
+ console.error('Auth status check failed:', error);
74
+ setError('Failed to check authentication status');
75
+ } finally {
76
+ setIsLoading(false);
77
+ }
78
+ };
79
+
80
+ const login = async (username, password) => {
81
+ try {
82
+ setError(null);
83
+ const response = await api.auth.login(username, password);
84
+
85
+ const data = await response.json();
86
+
87
+ if (response.ok) {
88
+ setToken(data.token);
89
+ setUser(data.user);
90
+ localStorage.setItem('auth-token', data.token);
91
+ return { success: true };
92
+ } else {
93
+ setError(data.error || 'Login failed');
94
+ return { success: false, error: data.error || 'Login failed' };
95
+ }
96
+ } catch (error) {
97
+ console.error('Login error:', error);
98
+ const errorMessage = 'Network error. Please try again.';
99
+ setError(errorMessage);
100
+ return { success: false, error: errorMessage };
101
+ }
102
+ };
103
+
104
+ const register = async (username, password) => {
105
+ try {
106
+ setError(null);
107
+ const response = await api.auth.register(username, password);
108
+
109
+ const data = await response.json();
110
+
111
+ if (response.ok) {
112
+ setToken(data.token);
113
+ setUser(data.user);
114
+ setNeedsSetup(false);
115
+ localStorage.setItem('auth-token', data.token);
116
+ return { success: true };
117
+ } else {
118
+ setError(data.error || 'Registration failed');
119
+ return { success: false, error: data.error || 'Registration failed' };
120
+ }
121
+ } catch (error) {
122
+ console.error('Registration error:', error);
123
+ const errorMessage = 'Network error. Please try again.';
124
+ setError(errorMessage);
125
+ return { success: false, error: errorMessage };
126
+ }
127
+ };
128
+
129
+ const logout = () => {
130
+ setToken(null);
131
+ setUser(null);
132
+ localStorage.removeItem('auth-token');
133
+
134
+ // Optional: Call logout endpoint for logging
135
+ if (token) {
136
+ api.auth.logout().catch(error => {
137
+ console.error('Logout endpoint error:', error);
138
+ });
139
+ }
140
+ };
141
+
142
+ const value = {
143
+ user,
144
+ token,
145
+ login,
146
+ register,
147
+ logout,
148
+ isLoading,
149
+ needsSetup,
150
+ error
151
+ };
152
+
153
+ return (
154
+ <AuthContext.Provider value={value}>
155
+ {children}
156
+ </AuthContext.Provider>
157
+ );
158
+ };