@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.
- package/dist/assets/index-BNGSzSdr.css +32 -0
- package/dist/assets/index-BZctMHnE.js +900 -0
- package/{index.html → dist/index.html} +4 -3
- package/package.json +6 -1
- package/server/database/auth.db +0 -0
- package/.env.example +0 -12
- package/.nvmrc +0 -1
- package/postcss.config.js +0 -6
- package/src/App.jsx +0 -751
- package/src/components/ChatInterface.jsx +0 -3485
- package/src/components/ClaudeLogo.jsx +0 -11
- package/src/components/ClaudeStatus.jsx +0 -107
- package/src/components/CodeEditor.jsx +0 -422
- package/src/components/CreateTaskModal.jsx +0 -88
- package/src/components/CursorLogo.jsx +0 -9
- package/src/components/DarkModeToggle.jsx +0 -35
- package/src/components/DiffViewer.jsx +0 -41
- package/src/components/ErrorBoundary.jsx +0 -73
- package/src/components/FileTree.jsx +0 -480
- package/src/components/GitPanel.jsx +0 -1283
- package/src/components/ImageViewer.jsx +0 -54
- package/src/components/LoginForm.jsx +0 -110
- package/src/components/MainContent.jsx +0 -577
- package/src/components/MicButton.jsx +0 -272
- package/src/components/MobileNav.jsx +0 -88
- package/src/components/NextTaskBanner.jsx +0 -695
- package/src/components/PRDEditor.jsx +0 -871
- package/src/components/ProtectedRoute.jsx +0 -44
- package/src/components/QuickSettingsPanel.jsx +0 -262
- package/src/components/Settings.jsx +0 -2023
- package/src/components/SetupForm.jsx +0 -135
- package/src/components/Shell.jsx +0 -663
- package/src/components/Sidebar.jsx +0 -1665
- package/src/components/StandaloneShell.jsx +0 -106
- package/src/components/TaskCard.jsx +0 -210
- package/src/components/TaskDetail.jsx +0 -406
- package/src/components/TaskIndicator.jsx +0 -108
- package/src/components/TaskList.jsx +0 -1054
- package/src/components/TaskMasterSetupWizard.jsx +0 -603
- package/src/components/TaskMasterStatus.jsx +0 -86
- package/src/components/TodoList.jsx +0 -91
- package/src/components/Tooltip.jsx +0 -91
- package/src/components/ui/badge.jsx +0 -31
- package/src/components/ui/button.jsx +0 -46
- package/src/components/ui/input.jsx +0 -19
- package/src/components/ui/scroll-area.jsx +0 -23
- package/src/contexts/AuthContext.jsx +0 -158
- package/src/contexts/TaskMasterContext.jsx +0 -324
- package/src/contexts/TasksSettingsContext.jsx +0 -95
- package/src/contexts/ThemeContext.jsx +0 -94
- package/src/contexts/WebSocketContext.jsx +0 -29
- package/src/hooks/useAudioRecorder.js +0 -109
- package/src/hooks/useVersionCheck.js +0 -39
- package/src/index.css +0 -822
- package/src/lib/utils.js +0 -6
- package/src/main.jsx +0 -10
- package/src/utils/api.js +0 -141
- package/src/utils/websocket.js +0 -109
- package/src/utils/whisper.js +0 -37
- package/tailwind.config.js +0 -63
- package/vite.config.js +0 -29
- /package/{public → dist}/convert-icons.md +0 -0
- /package/{public → dist}/favicon.png +0 -0
- /package/{public → dist}/favicon.svg +0 -0
- /package/{public → dist}/generate-icons.js +0 -0
- /package/{public → dist}/icons/claude-ai-icon.svg +0 -0
- /package/{public → dist}/icons/cursor.svg +0 -0
- /package/{public → dist}/icons/generate-icons.md +0 -0
- /package/{public → dist}/icons/icon-128x128.png +0 -0
- /package/{public → dist}/icons/icon-128x128.svg +0 -0
- /package/{public → dist}/icons/icon-144x144.png +0 -0
- /package/{public → dist}/icons/icon-144x144.svg +0 -0
- /package/{public → dist}/icons/icon-152x152.png +0 -0
- /package/{public → dist}/icons/icon-152x152.svg +0 -0
- /package/{public → dist}/icons/icon-192x192.png +0 -0
- /package/{public → dist}/icons/icon-192x192.svg +0 -0
- /package/{public → dist}/icons/icon-384x384.png +0 -0
- /package/{public → dist}/icons/icon-384x384.svg +0 -0
- /package/{public → dist}/icons/icon-512x512.png +0 -0
- /package/{public → dist}/icons/icon-512x512.svg +0 -0
- /package/{public → dist}/icons/icon-72x72.png +0 -0
- /package/{public → dist}/icons/icon-72x72.svg +0 -0
- /package/{public → dist}/icons/icon-96x96.png +0 -0
- /package/{public → dist}/icons/icon-96x96.svg +0 -0
- /package/{public → dist}/icons/icon-template.svg +0 -0
- /package/{public → dist}/logo.svg +0 -0
- /package/{public → dist}/manifest.json +0 -0
- /package/{public → dist}/screenshots/cli-selection.png +0 -0
- /package/{public → dist}/screenshots/desktop-main.png +0 -0
- /package/{public → dist}/screenshots/mobile-chat.png +0 -0
- /package/{public → dist}/screenshots/tools-modal.png +0 -0
- /package/{public → dist}/sw.js +0 -0
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
|
|
2
|
-
import { api } from '../utils/api';
|
|
3
|
-
import { useAuth } from './AuthContext';
|
|
4
|
-
import { useWebSocketContext } from './WebSocketContext';
|
|
5
|
-
|
|
6
|
-
const TaskMasterContext = createContext({
|
|
7
|
-
// TaskMaster project state
|
|
8
|
-
projects: [],
|
|
9
|
-
currentProject: null,
|
|
10
|
-
projectTaskMaster: null,
|
|
11
|
-
|
|
12
|
-
// MCP server state
|
|
13
|
-
mcpServerStatus: null,
|
|
14
|
-
|
|
15
|
-
// Tasks state
|
|
16
|
-
tasks: [],
|
|
17
|
-
nextTask: null,
|
|
18
|
-
|
|
19
|
-
// Loading states
|
|
20
|
-
isLoading: false,
|
|
21
|
-
isLoadingTasks: false,
|
|
22
|
-
isLoadingMCP: false,
|
|
23
|
-
|
|
24
|
-
// Error state
|
|
25
|
-
error: null,
|
|
26
|
-
|
|
27
|
-
// Actions
|
|
28
|
-
refreshProjects: () => {},
|
|
29
|
-
setCurrentProject: () => {},
|
|
30
|
-
refreshTasks: () => {},
|
|
31
|
-
refreshMCPStatus: () => {},
|
|
32
|
-
clearError: () => {}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export const useTaskMaster = () => {
|
|
36
|
-
const context = useContext(TaskMasterContext);
|
|
37
|
-
if (!context) {
|
|
38
|
-
throw new Error('useTaskMaster must be used within a TaskMasterProvider');
|
|
39
|
-
}
|
|
40
|
-
return context;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const TaskMasterProvider = ({ children }) => {
|
|
44
|
-
// Get WebSocket messages from shared context to avoid duplicate connections
|
|
45
|
-
const { messages } = useWebSocketContext();
|
|
46
|
-
|
|
47
|
-
// Authentication context
|
|
48
|
-
const { user, token, isLoading: authLoading } = useAuth();
|
|
49
|
-
|
|
50
|
-
// State
|
|
51
|
-
const [projects, setProjects] = useState([]);
|
|
52
|
-
const [currentProject, setCurrentProjectState] = useState(null);
|
|
53
|
-
const [projectTaskMaster, setProjectTaskMaster] = useState(null);
|
|
54
|
-
const [mcpServerStatus, setMCPServerStatus] = useState(null);
|
|
55
|
-
const [tasks, setTasks] = useState([]);
|
|
56
|
-
const [nextTask, setNextTask] = useState(null);
|
|
57
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
58
|
-
const [isLoadingTasks, setIsLoadingTasks] = useState(false);
|
|
59
|
-
const [isLoadingMCP, setIsLoadingMCP] = useState(false);
|
|
60
|
-
const [error, setError] = useState(null);
|
|
61
|
-
|
|
62
|
-
// Helper to handle API errors
|
|
63
|
-
const handleError = (error, context) => {
|
|
64
|
-
console.error(`TaskMaster ${context} error:`, error);
|
|
65
|
-
setError({
|
|
66
|
-
message: error.message || `Failed to ${context}`,
|
|
67
|
-
context,
|
|
68
|
-
timestamp: new Date().toISOString()
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// Clear error state
|
|
73
|
-
const clearError = useCallback(() => {
|
|
74
|
-
setError(null);
|
|
75
|
-
}, []);
|
|
76
|
-
|
|
77
|
-
// This will be defined after the functions are declared
|
|
78
|
-
|
|
79
|
-
// Refresh projects with TaskMaster metadata
|
|
80
|
-
const refreshProjects = useCallback(async () => {
|
|
81
|
-
// Only make API calls if user is authenticated
|
|
82
|
-
if (!user || !token) {
|
|
83
|
-
setProjects([]);
|
|
84
|
-
setCurrentProjectState(null); // This might be the problem!
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
setIsLoading(true);
|
|
90
|
-
clearError();
|
|
91
|
-
const response = await api.get('/projects');
|
|
92
|
-
|
|
93
|
-
if (!response.ok) {
|
|
94
|
-
throw new Error(`Failed to fetch projects: ${response.status}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const projectsData = await response.json();
|
|
98
|
-
|
|
99
|
-
// Check if projectsData is an array
|
|
100
|
-
if (!Array.isArray(projectsData)) {
|
|
101
|
-
console.error('Projects API returned non-array data:', projectsData);
|
|
102
|
-
setProjects([]);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Filter and enrich projects with TaskMaster data
|
|
107
|
-
const enrichedProjects = projectsData.map(project => ({
|
|
108
|
-
...project,
|
|
109
|
-
taskMasterConfigured: project.taskmaster?.hasTaskmaster || false,
|
|
110
|
-
taskMasterStatus: project.taskmaster?.status || 'not-configured',
|
|
111
|
-
taskCount: project.taskmaster?.metadata?.taskCount || 0,
|
|
112
|
-
completedCount: project.taskmaster?.metadata?.completed || 0
|
|
113
|
-
}));
|
|
114
|
-
|
|
115
|
-
setProjects(enrichedProjects);
|
|
116
|
-
|
|
117
|
-
// If current project is set, update its TaskMaster data
|
|
118
|
-
if (currentProject) {
|
|
119
|
-
const updatedCurrent = enrichedProjects.find(p => p.name === currentProject.name);
|
|
120
|
-
if (updatedCurrent) {
|
|
121
|
-
setCurrentProjectState(updatedCurrent);
|
|
122
|
-
setProjectTaskMaster(updatedCurrent.taskmaster);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
} catch (err) {
|
|
126
|
-
handleError(err, 'load projects');
|
|
127
|
-
} finally {
|
|
128
|
-
setIsLoading(false);
|
|
129
|
-
}
|
|
130
|
-
}, [user, token]); // Remove currentProject dependency to avoid infinite loops
|
|
131
|
-
|
|
132
|
-
// Set current project and load its TaskMaster details
|
|
133
|
-
const setCurrentProject = useCallback(async (project) => {
|
|
134
|
-
try {
|
|
135
|
-
setCurrentProjectState(project);
|
|
136
|
-
|
|
137
|
-
// Clear previous project's data immediately when switching projects
|
|
138
|
-
setTasks([]);
|
|
139
|
-
setNextTask(null);
|
|
140
|
-
setProjectTaskMaster(null); // Clear previous TaskMaster data
|
|
141
|
-
|
|
142
|
-
// Try to fetch fresh TaskMaster detection data for the project
|
|
143
|
-
if (project?.name) {
|
|
144
|
-
try {
|
|
145
|
-
const response = await api.get(`/taskmaster/detect/${encodeURIComponent(project.name)}`);
|
|
146
|
-
if (response.ok) {
|
|
147
|
-
const detectionData = await response.json();
|
|
148
|
-
setProjectTaskMaster(detectionData.taskmaster);
|
|
149
|
-
} else {
|
|
150
|
-
// If individual detection fails, fall back to project data from /api/projects
|
|
151
|
-
console.warn('Individual TaskMaster detection failed, using project data:', response.status);
|
|
152
|
-
setProjectTaskMaster(project.taskmaster || null);
|
|
153
|
-
}
|
|
154
|
-
} catch (detectionError) {
|
|
155
|
-
// If individual detection fails, fall back to project data from /api/projects
|
|
156
|
-
console.warn('TaskMaster detection error, using project data:', detectionError.message);
|
|
157
|
-
setProjectTaskMaster(project.taskmaster || null);
|
|
158
|
-
}
|
|
159
|
-
} else {
|
|
160
|
-
setProjectTaskMaster(null);
|
|
161
|
-
}
|
|
162
|
-
} catch (err) {
|
|
163
|
-
console.error('Error in setCurrentProject:', err);
|
|
164
|
-
handleError(err, 'set current project');
|
|
165
|
-
// Fall back to project data if available
|
|
166
|
-
setProjectTaskMaster(project?.taskmaster || null);
|
|
167
|
-
}
|
|
168
|
-
}, []);
|
|
169
|
-
|
|
170
|
-
// Refresh MCP server status
|
|
171
|
-
const refreshMCPStatus = useCallback(async () => {
|
|
172
|
-
// Only make API calls if user is authenticated
|
|
173
|
-
if (!user || !token) {
|
|
174
|
-
setMCPServerStatus(null);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
setIsLoadingMCP(true);
|
|
180
|
-
clearError();
|
|
181
|
-
const mcpStatus = await api.get('/mcp-utils/taskmaster-server');
|
|
182
|
-
setMCPServerStatus(mcpStatus);
|
|
183
|
-
} catch (err) {
|
|
184
|
-
handleError(err, 'check MCP server status');
|
|
185
|
-
} finally {
|
|
186
|
-
setIsLoadingMCP(false);
|
|
187
|
-
}
|
|
188
|
-
}, [user, token]);
|
|
189
|
-
|
|
190
|
-
// Refresh tasks for current project - load real TaskMaster data
|
|
191
|
-
const refreshTasks = useCallback(async () => {
|
|
192
|
-
if (!currentProject) {
|
|
193
|
-
setTasks([]);
|
|
194
|
-
setNextTask(null);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Only make API calls if user is authenticated
|
|
199
|
-
if (!user || !token) {
|
|
200
|
-
setTasks([]);
|
|
201
|
-
setNextTask(null);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
setIsLoadingTasks(true);
|
|
207
|
-
clearError();
|
|
208
|
-
|
|
209
|
-
// Load tasks from the TaskMaster API endpoint
|
|
210
|
-
const response = await api.get(`/taskmaster/tasks/${encodeURIComponent(currentProject.name)}`);
|
|
211
|
-
|
|
212
|
-
if (!response.ok) {
|
|
213
|
-
const errorData = await response.json();
|
|
214
|
-
throw new Error(errorData.message || 'Failed to load tasks');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const data = await response.json();
|
|
218
|
-
|
|
219
|
-
setTasks(data.tasks || []);
|
|
220
|
-
|
|
221
|
-
// Find next task (pending or in-progress)
|
|
222
|
-
const nextTask = data.tasks?.find(task =>
|
|
223
|
-
task.status === 'pending' || task.status === 'in-progress'
|
|
224
|
-
) || null;
|
|
225
|
-
setNextTask(nextTask);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
} catch (err) {
|
|
229
|
-
console.error('Error loading tasks:', err);
|
|
230
|
-
handleError(err, 'load tasks');
|
|
231
|
-
// Set empty state on error
|
|
232
|
-
setTasks([]);
|
|
233
|
-
setNextTask(null);
|
|
234
|
-
} finally {
|
|
235
|
-
setIsLoadingTasks(false);
|
|
236
|
-
}
|
|
237
|
-
}, [currentProject, user, token]);
|
|
238
|
-
|
|
239
|
-
// Load initial data on mount or when auth changes
|
|
240
|
-
useEffect(() => {
|
|
241
|
-
if (!authLoading && user && token) {
|
|
242
|
-
refreshProjects();
|
|
243
|
-
refreshMCPStatus();
|
|
244
|
-
} else {
|
|
245
|
-
console.log('Auth not ready or no user, skipping project load:', { authLoading, user: !!user, token: !!token });
|
|
246
|
-
}
|
|
247
|
-
}, [refreshProjects, refreshMCPStatus, authLoading, user, token]);
|
|
248
|
-
|
|
249
|
-
// Clear errors when authentication changes
|
|
250
|
-
useEffect(() => {
|
|
251
|
-
if (user && token) {
|
|
252
|
-
clearError();
|
|
253
|
-
}
|
|
254
|
-
}, [user, token, clearError]);
|
|
255
|
-
|
|
256
|
-
// Refresh tasks when current project changes
|
|
257
|
-
useEffect(() => {
|
|
258
|
-
if (currentProject?.name && user && token) {
|
|
259
|
-
refreshTasks();
|
|
260
|
-
}
|
|
261
|
-
}, [currentProject?.name, user, token, refreshTasks]);
|
|
262
|
-
|
|
263
|
-
// Handle WebSocket messages for TaskMaster updates
|
|
264
|
-
useEffect(() => {
|
|
265
|
-
const latestMessage = messages[messages.length - 1];
|
|
266
|
-
if (!latestMessage) return;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
switch (latestMessage.type) {
|
|
270
|
-
case 'taskmaster-project-updated':
|
|
271
|
-
// Refresh projects when TaskMaster state changes
|
|
272
|
-
if (latestMessage.projectName) {
|
|
273
|
-
refreshProjects();
|
|
274
|
-
}
|
|
275
|
-
break;
|
|
276
|
-
|
|
277
|
-
case 'taskmaster-tasks-updated':
|
|
278
|
-
// Refresh tasks for the current project
|
|
279
|
-
if (latestMessage.projectName === currentProject?.name) {
|
|
280
|
-
refreshTasks();
|
|
281
|
-
}
|
|
282
|
-
break;
|
|
283
|
-
|
|
284
|
-
case 'taskmaster-mcp-status-changed':
|
|
285
|
-
// Refresh MCP server status
|
|
286
|
-
refreshMCPStatus();
|
|
287
|
-
break;
|
|
288
|
-
|
|
289
|
-
default:
|
|
290
|
-
// Ignore non-TaskMaster messages
|
|
291
|
-
break;
|
|
292
|
-
}
|
|
293
|
-
}, [messages, refreshProjects, refreshTasks, refreshMCPStatus, currentProject]);
|
|
294
|
-
|
|
295
|
-
// Context value
|
|
296
|
-
const contextValue = {
|
|
297
|
-
// State
|
|
298
|
-
projects,
|
|
299
|
-
currentProject,
|
|
300
|
-
projectTaskMaster,
|
|
301
|
-
mcpServerStatus,
|
|
302
|
-
tasks,
|
|
303
|
-
nextTask,
|
|
304
|
-
isLoading,
|
|
305
|
-
isLoadingTasks,
|
|
306
|
-
isLoadingMCP,
|
|
307
|
-
error,
|
|
308
|
-
|
|
309
|
-
// Actions
|
|
310
|
-
refreshProjects,
|
|
311
|
-
setCurrentProject,
|
|
312
|
-
refreshTasks,
|
|
313
|
-
refreshMCPStatus,
|
|
314
|
-
clearError
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
return (
|
|
318
|
-
<TaskMasterContext.Provider value={contextValue}>
|
|
319
|
-
{children}
|
|
320
|
-
</TaskMasterContext.Provider>
|
|
321
|
-
);
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
export default TaskMasterContext;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, useEffect } from 'react';
|
|
2
|
-
import { api } from '../utils/api';
|
|
3
|
-
|
|
4
|
-
const TasksSettingsContext = createContext({
|
|
5
|
-
tasksEnabled: true,
|
|
6
|
-
setTasksEnabled: () => {},
|
|
7
|
-
toggleTasksEnabled: () => {},
|
|
8
|
-
isTaskMasterInstalled: null,
|
|
9
|
-
isTaskMasterReady: null,
|
|
10
|
-
installationStatus: null,
|
|
11
|
-
isCheckingInstallation: true
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export const useTasksSettings = () => {
|
|
15
|
-
const context = useContext(TasksSettingsContext);
|
|
16
|
-
if (!context) {
|
|
17
|
-
throw new Error('useTasksSettings must be used within a TasksSettingsProvider');
|
|
18
|
-
}
|
|
19
|
-
return context;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const TasksSettingsProvider = ({ children }) => {
|
|
23
|
-
const [tasksEnabled, setTasksEnabled] = useState(() => {
|
|
24
|
-
// Load from localStorage on initialization
|
|
25
|
-
const saved = localStorage.getItem('tasks-enabled');
|
|
26
|
-
return saved !== null ? JSON.parse(saved) : true; // Default to true
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const [isTaskMasterInstalled, setIsTaskMasterInstalled] = useState(null);
|
|
30
|
-
const [isTaskMasterReady, setIsTaskMasterReady] = useState(null);
|
|
31
|
-
const [installationStatus, setInstallationStatus] = useState(null);
|
|
32
|
-
const [isCheckingInstallation, setIsCheckingInstallation] = useState(true);
|
|
33
|
-
|
|
34
|
-
// Save to localStorage whenever tasksEnabled changes
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
localStorage.setItem('tasks-enabled', JSON.stringify(tasksEnabled));
|
|
37
|
-
}, [tasksEnabled]);
|
|
38
|
-
|
|
39
|
-
// Check TaskMaster installation status asynchronously on component mount
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
const checkInstallation = async () => {
|
|
42
|
-
try {
|
|
43
|
-
const response = await api.get('/taskmaster/installation-status');
|
|
44
|
-
if (response.ok) {
|
|
45
|
-
const data = await response.json();
|
|
46
|
-
setInstallationStatus(data);
|
|
47
|
-
setIsTaskMasterInstalled(data.installation?.isInstalled || false);
|
|
48
|
-
setIsTaskMasterReady(data.isReady || false);
|
|
49
|
-
|
|
50
|
-
// If TaskMaster is not installed and user hasn't explicitly enabled tasks,
|
|
51
|
-
// disable tasks automatically
|
|
52
|
-
const userEnabledTasks = localStorage.getItem('tasks-enabled');
|
|
53
|
-
if (!data.installation?.isInstalled && !userEnabledTasks) {
|
|
54
|
-
setTasksEnabled(false);
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
console.error('Failed to check TaskMaster installation status');
|
|
58
|
-
setIsTaskMasterInstalled(false);
|
|
59
|
-
setIsTaskMasterReady(false);
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('Error checking TaskMaster installation:', error);
|
|
63
|
-
setIsTaskMasterInstalled(false);
|
|
64
|
-
setIsTaskMasterReady(false);
|
|
65
|
-
} finally {
|
|
66
|
-
setIsCheckingInstallation(false);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Run check asynchronously without blocking initial render
|
|
71
|
-
setTimeout(checkInstallation, 0);
|
|
72
|
-
}, []);
|
|
73
|
-
|
|
74
|
-
const toggleTasksEnabled = () => {
|
|
75
|
-
setTasksEnabled(prev => !prev);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const contextValue = {
|
|
79
|
-
tasksEnabled,
|
|
80
|
-
setTasksEnabled,
|
|
81
|
-
toggleTasksEnabled,
|
|
82
|
-
isTaskMasterInstalled,
|
|
83
|
-
isTaskMasterReady,
|
|
84
|
-
installationStatus,
|
|
85
|
-
isCheckingInstallation
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<TasksSettingsContext.Provider value={contextValue}>
|
|
90
|
-
{children}
|
|
91
|
-
</TasksSettingsContext.Provider>
|
|
92
|
-
);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
export default TasksSettingsContext;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
const ThemeContext = createContext();
|
|
4
|
-
|
|
5
|
-
export const useTheme = () => {
|
|
6
|
-
const context = useContext(ThemeContext);
|
|
7
|
-
if (!context) {
|
|
8
|
-
throw new Error('useTheme must be used within a ThemeProvider');
|
|
9
|
-
}
|
|
10
|
-
return context;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const ThemeProvider = ({ children }) => {
|
|
14
|
-
// Check for saved theme preference or default to system preference
|
|
15
|
-
const [isDarkMode, setIsDarkMode] = useState(() => {
|
|
16
|
-
// Check localStorage first
|
|
17
|
-
const savedTheme = localStorage.getItem('theme');
|
|
18
|
-
if (savedTheme) {
|
|
19
|
-
return savedTheme === 'dark';
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Check system preference
|
|
23
|
-
if (window.matchMedia) {
|
|
24
|
-
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return false;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// Update document class and localStorage when theme changes
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
if (isDarkMode) {
|
|
33
|
-
document.documentElement.classList.add('dark');
|
|
34
|
-
localStorage.setItem('theme', 'dark');
|
|
35
|
-
|
|
36
|
-
// Update iOS status bar style and theme color for dark mode
|
|
37
|
-
const statusBarMeta = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]');
|
|
38
|
-
if (statusBarMeta) {
|
|
39
|
-
statusBarMeta.setAttribute('content', 'black-translucent');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
|
|
43
|
-
if (themeColorMeta) {
|
|
44
|
-
themeColorMeta.setAttribute('content', '#0c1117'); // Dark background color (hsl(222.2 84% 4.9%))
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
document.documentElement.classList.remove('dark');
|
|
48
|
-
localStorage.setItem('theme', 'light');
|
|
49
|
-
|
|
50
|
-
// Update iOS status bar style and theme color for light mode
|
|
51
|
-
const statusBarMeta = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]');
|
|
52
|
-
if (statusBarMeta) {
|
|
53
|
-
statusBarMeta.setAttribute('content', 'default');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const themeColorMeta = document.querySelector('meta[name="theme-color"]');
|
|
57
|
-
if (themeColorMeta) {
|
|
58
|
-
themeColorMeta.setAttribute('content', '#ffffff'); // Light background color
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}, [isDarkMode]);
|
|
62
|
-
|
|
63
|
-
// Listen for system theme changes
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
if (!window.matchMedia) return;
|
|
66
|
-
|
|
67
|
-
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
68
|
-
const handleChange = (e) => {
|
|
69
|
-
// Only update if user hasn't manually set a preference
|
|
70
|
-
const savedTheme = localStorage.getItem('theme');
|
|
71
|
-
if (!savedTheme) {
|
|
72
|
-
setIsDarkMode(e.matches);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
mediaQuery.addEventListener('change', handleChange);
|
|
77
|
-
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
78
|
-
}, []);
|
|
79
|
-
|
|
80
|
-
const toggleDarkMode = () => {
|
|
81
|
-
setIsDarkMode(prev => !prev);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const value = {
|
|
85
|
-
isDarkMode,
|
|
86
|
-
toggleDarkMode,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<ThemeContext.Provider value={value}>
|
|
91
|
-
{children}
|
|
92
|
-
</ThemeContext.Provider>
|
|
93
|
-
);
|
|
94
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext } from 'react';
|
|
2
|
-
import { useWebSocket } from '../utils/websocket';
|
|
3
|
-
|
|
4
|
-
const WebSocketContext = createContext({
|
|
5
|
-
ws: null,
|
|
6
|
-
sendMessage: () => {},
|
|
7
|
-
messages: [],
|
|
8
|
-
isConnected: false
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export const useWebSocketContext = () => {
|
|
12
|
-
const context = useContext(WebSocketContext);
|
|
13
|
-
if (!context) {
|
|
14
|
-
throw new Error('useWebSocketContext must be used within a WebSocketProvider');
|
|
15
|
-
}
|
|
16
|
-
return context;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const WebSocketProvider = ({ children }) => {
|
|
20
|
-
const webSocketData = useWebSocket();
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<WebSocketContext.Provider value={webSocketData}>
|
|
24
|
-
{children}
|
|
25
|
-
</WebSocketContext.Provider>
|
|
26
|
-
);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export default WebSocketContext;
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { useState, useRef, useCallback } from 'react';
|
|
2
|
-
|
|
3
|
-
export function useAudioRecorder() {
|
|
4
|
-
const [isRecording, setRecording] = useState(false);
|
|
5
|
-
const [audioBlob, setAudioBlob] = useState(null);
|
|
6
|
-
const [error, setError] = useState(null);
|
|
7
|
-
const mediaRecorderRef = useRef(null);
|
|
8
|
-
const streamRef = useRef(null);
|
|
9
|
-
const chunksRef = useRef([]);
|
|
10
|
-
|
|
11
|
-
const start = useCallback(async () => {
|
|
12
|
-
try {
|
|
13
|
-
setError(null);
|
|
14
|
-
setAudioBlob(null);
|
|
15
|
-
chunksRef.current = [];
|
|
16
|
-
|
|
17
|
-
// Request microphone access
|
|
18
|
-
const stream = await navigator.mediaDevices.getUserMedia({
|
|
19
|
-
audio: {
|
|
20
|
-
echoCancellation: true,
|
|
21
|
-
noiseSuppression: true,
|
|
22
|
-
sampleRate: 16000,
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
streamRef.current = stream;
|
|
27
|
-
|
|
28
|
-
// Determine supported MIME type
|
|
29
|
-
const mimeType = MediaRecorder.isTypeSupported('audio/webm')
|
|
30
|
-
? 'audio/webm'
|
|
31
|
-
: 'audio/mp4';
|
|
32
|
-
|
|
33
|
-
// Create media recorder
|
|
34
|
-
const recorder = new MediaRecorder(stream, { mimeType });
|
|
35
|
-
mediaRecorderRef.current = recorder;
|
|
36
|
-
|
|
37
|
-
// Set up event handlers
|
|
38
|
-
recorder.ondataavailable = (e) => {
|
|
39
|
-
if (e.data.size > 0) {
|
|
40
|
-
chunksRef.current.push(e.data);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
recorder.onstop = () => {
|
|
45
|
-
// Create blob from chunks
|
|
46
|
-
const blob = new Blob(chunksRef.current, { type: mimeType });
|
|
47
|
-
setAudioBlob(blob);
|
|
48
|
-
|
|
49
|
-
// Clean up stream
|
|
50
|
-
if (streamRef.current) {
|
|
51
|
-
streamRef.current.getTracks().forEach(track => track.stop());
|
|
52
|
-
streamRef.current = null;
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
recorder.onerror = (event) => {
|
|
57
|
-
console.error('MediaRecorder error:', event);
|
|
58
|
-
setError('Recording failed');
|
|
59
|
-
setRecording(false);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Start recording
|
|
63
|
-
recorder.start();
|
|
64
|
-
setRecording(true);
|
|
65
|
-
console.log('Recording started');
|
|
66
|
-
} catch (err) {
|
|
67
|
-
console.error('Failed to start recording:', err);
|
|
68
|
-
setError(err.message || 'Failed to start recording');
|
|
69
|
-
setRecording(false);
|
|
70
|
-
}
|
|
71
|
-
}, []);
|
|
72
|
-
|
|
73
|
-
const stop = useCallback(() => {
|
|
74
|
-
console.log('Stop called, recorder state:', mediaRecorderRef.current?.state);
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
|
|
78
|
-
mediaRecorderRef.current.stop();
|
|
79
|
-
console.log('Recording stopped');
|
|
80
|
-
}
|
|
81
|
-
} catch (err) {
|
|
82
|
-
console.error('Error stopping recorder:', err);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Always update state
|
|
86
|
-
setRecording(false);
|
|
87
|
-
|
|
88
|
-
// Clean up stream if still active
|
|
89
|
-
if (streamRef.current) {
|
|
90
|
-
streamRef.current.getTracks().forEach(track => track.stop());
|
|
91
|
-
streamRef.current = null;
|
|
92
|
-
}
|
|
93
|
-
}, []);
|
|
94
|
-
|
|
95
|
-
const reset = useCallback(() => {
|
|
96
|
-
setAudioBlob(null);
|
|
97
|
-
setError(null);
|
|
98
|
-
chunksRef.current = [];
|
|
99
|
-
}, []);
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
isRecording,
|
|
103
|
-
audioBlob,
|
|
104
|
-
error,
|
|
105
|
-
start,
|
|
106
|
-
stop,
|
|
107
|
-
reset
|
|
108
|
-
};
|
|
109
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
// hooks/useVersionCheck.js
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { version } from '../../package.json';
|
|
4
|
-
|
|
5
|
-
export const useVersionCheck = (owner, repo) => {
|
|
6
|
-
const [updateAvailable, setUpdateAvailable] = useState(false);
|
|
7
|
-
const [latestVersion, setLatestVersion] = useState(null);
|
|
8
|
-
|
|
9
|
-
useEffect(() => {
|
|
10
|
-
const checkVersion = async () => {
|
|
11
|
-
try {
|
|
12
|
-
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases/latest`);
|
|
13
|
-
const data = await response.json();
|
|
14
|
-
|
|
15
|
-
// Handle the case where there might not be any releases
|
|
16
|
-
if (data.tag_name) {
|
|
17
|
-
const latest = data.tag_name.replace(/^v/, '');
|
|
18
|
-
setLatestVersion(latest);
|
|
19
|
-
setUpdateAvailable(version !== latest);
|
|
20
|
-
} else {
|
|
21
|
-
// No releases found, don't show update notification
|
|
22
|
-
setUpdateAvailable(false);
|
|
23
|
-
setLatestVersion(null);
|
|
24
|
-
}
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error('Version check failed:', error);
|
|
27
|
-
// On error, don't show update notification
|
|
28
|
-
setUpdateAvailable(false);
|
|
29
|
-
setLatestVersion(null);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
checkVersion();
|
|
34
|
-
const interval = setInterval(checkVersion, 5 * 60 * 1000); // Check every 5 minutes
|
|
35
|
-
return () => clearInterval(interval);
|
|
36
|
-
}, [owner, repo]);
|
|
37
|
-
|
|
38
|
-
return { updateAvailable, latestVersion, currentVersion: version };
|
|
39
|
-
};
|