refactor: Restructure files and folders to better mimic feature-based architecture

This commit is contained in:
Haileyesus
2026-02-11 18:11:06 +03:00
parent fadbcc8259
commit 7f45540dbe
34 changed files with 124 additions and 233 deletions

View File

@@ -2,8 +2,8 @@ import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import Sidebar from '../Sidebar';
import MainContent from '../MainContent';
import Sidebar from '../sidebar/view/Sidebar';
import MainContent from '../main-content/view/MainContent';
import MobileNav from '../MobileNav';
import { useWebSocket } from '../../contexts/WebSocketContext';

View File

@@ -10,19 +10,19 @@ import type {
TouchEvent,
} from 'react';
import { useDropzone } from 'react-dropzone';
import { authenticatedFetch } from '../../utils/api';
import { thinkingModes } from '../../components/ThinkingModeSelector.jsx';
import { grantClaudeToolPermission } from '../../components/chat/utils/chatPermissions';
import { safeLocalStorage } from '../../components/chat/utils/chatStorage';
import { authenticatedFetch } from '../../../utils/api';
import { thinkingModes } from '../../ThinkingModeSelector.jsx';
import { grantClaudeToolPermission } from '../utils/chatPermissions';
import { safeLocalStorage } from '../utils/chatStorage';
import type {
ChatMessage,
PendingPermissionRequest,
PermissionMode,
Provider,
} from '../../components/chat/types';
} from '../types';
import { useFileMentions } from './useFileMentions';
import { type SlashCommand, useSlashCommands } from './useSlashCommands';
import type { Project, ProjectSession } from '../../types/app';
import type { Project, ProjectSession } from '../../../types/app';
type PendingViewSession = {
sessionId: string | null;

View File

@@ -1,8 +1,8 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { authenticatedFetch } from '../../utils/api';
import { CLAUDE_MODELS, CODEX_MODELS, CURSOR_MODELS } from '../../../shared/modelConstants';
import type { PendingPermissionRequest, PermissionMode, Provider } from '../../components/chat/types';
import type { ProjectSession } from '../../types/app';
import { authenticatedFetch } from '../../../utils/api';
import { CLAUDE_MODELS, CODEX_MODELS, CURSOR_MODELS } from '../../../../shared/modelConstants';
import type { PendingPermissionRequest, PermissionMode, Provider } from '../types';
import type { ProjectSession } from '../../../types/app';
interface UseChatProviderStateArgs {
selectedSession: ProjectSession | null;

View File

@@ -1,9 +1,9 @@
import { useEffect } from 'react';
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
import { decodeHtmlEntities, formatUsageLimitText } from '../../components/chat/utils/chatFormatting';
import { safeLocalStorage } from '../../components/chat/utils/chatStorage';
import type { ChatMessage, PendingPermissionRequest, Provider } from '../../components/chat/types';
import type { Project, ProjectSession } from '../../types/app';
import { decodeHtmlEntities, formatUsageLimitText } from '../utils/chatFormatting';
import { safeLocalStorage } from '../utils/chatStorage';
import type { ChatMessage, PendingPermissionRequest, Provider } from '../types';
import type { Project, ProjectSession } from '../../../types/app';
type PendingViewSession = {
sessionId: string | null;

View File

@@ -1,15 +1,15 @@
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { MutableRefObject } from 'react';
import { api, authenticatedFetch } from '../../utils/api';
import type { ChatMessage, Provider } from '../../components/chat/types';
import type { Project, ProjectSession } from '../../types/app';
import { safeLocalStorage } from '../../components/chat/utils/chatStorage';
import { api, authenticatedFetch } from '../../../utils/api';
import type { ChatMessage, Provider } from '../types';
import type { Project, ProjectSession } from '../../../types/app';
import { safeLocalStorage } from '../utils/chatStorage';
import {
convertCursorSessionMessages,
convertSessionMessages,
createCachedDiffCalculator,
type DiffCalculator,
} from '../../components/chat/utils/messageTransforms';
} from '../utils/messageTransforms';
const MESSAGES_PER_PAGE = 20;
const INITIAL_VISIBLE_MESSAGES = 100;

View File

@@ -1,8 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import type { Dispatch, KeyboardEvent, RefObject, SetStateAction } from 'react';
import { api } from '../../utils/api';
import { escapeRegExp } from '../../components/chat/utils/chatFormatting';
import type { Project } from '../../types/app';
import { api } from '../../../utils/api';
import { escapeRegExp } from '../utils/chatFormatting';
import type { Project } from '../../../types/app';
interface ProjectFileNode {
name: string;

View File

@@ -1,9 +1,9 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { Dispatch, KeyboardEvent, RefObject, SetStateAction } from 'react';
import Fuse from 'fuse.js';
import { authenticatedFetch } from '../../utils/api';
import { safeLocalStorage } from '../../components/chat/utils/chatStorage';
import type { Project } from '../../types/app';
import { authenticatedFetch } from '../../../utils/api';
import { safeLocalStorage } from '../utils/chatStorage';
import type { Project } from '../../../types/app';
const COMMAND_QUERY_DEBOUNCE_MS = 150;

View File

@@ -1,15 +1,15 @@
import React, { useCallback, useEffect, useRef } from 'react';
import QuickSettingsPanel from './QuickSettingsPanel';
import { useTasksSettings } from '../contexts/TasksSettingsContext';
import QuickSettingsPanel from '../../QuickSettingsPanel';
import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
import { useTranslation } from 'react-i18next';
import ChatMessagesPane from './chat/view/ChatMessagesPane';
import ChatComposer from './chat/view/ChatComposer';
import type { ChatInterfaceProps } from './chat/types';
import { useChatProviderState } from '../hooks/chat/useChatProviderState';
import { useChatSessionState } from '../hooks/chat/useChatSessionState';
import { useChatRealtimeHandlers } from '../hooks/chat/useChatRealtimeHandlers';
import { useChatComposerState } from '../hooks/chat/useChatComposerState';
import type { Provider } from './chat/types';
import ChatMessagesPane from './ChatMessagesPane';
import ChatComposer from './ChatComposer';
import type { ChatInterfaceProps } from '../types';
import { useChatProviderState } from '../hooks/useChatProviderState';
import { useChatSessionState } from '../hooks/useChatSessionState';
import { useChatRealtimeHandlers } from '../hooks/useChatRealtimeHandlers';
import { useChatComposerState } from '../hooks/useChatComposerState';
import type { Provider } from '../types';
type PendingViewSession = {
sessionId: string | null;

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import type { MouseEvent as ReactMouseEvent } from 'react';
import type { Project } from '../../types/app';
import type { DiffInfo, EditingFile } from '../../components/main-content/types';
import type { Project } from '../../../types/app';
import type { DiffInfo, EditingFile } from '../types/types';
type UseEditorSidebarOptions = {
selectedProject: Project | null;

View File

@@ -1,5 +1,5 @@
import type { Dispatch, MouseEvent, RefObject, SetStateAction } from 'react';
import type { AppTab, Project, ProjectSession } from '../../types/app';
import type { AppTab, Project, ProjectSession } from '../../../types/app';
export type SessionLifecycleHandler = (sessionId?: string | null) => void;

View File

@@ -1,22 +1,22 @@
import React, { useEffect } from 'react';
import ChatInterface from './ChatInterface';
import FileTree from './FileTree';
import StandaloneShell from './StandaloneShell';
import GitPanel from './GitPanel';
import ErrorBoundary from './ErrorBoundary';
import ChatInterface from '../../chat/view/ChatInterface';
import FileTree from '../../FileTree';
import StandaloneShell from '../../StandaloneShell';
import GitPanel from '../../GitPanel';
import ErrorBoundary from '../../ErrorBoundary';
import MainContentHeader from './main-content/MainContentHeader';
import MainContentStateView from './main-content/MainContentStateView';
import EditorSidebar from './main-content/EditorSidebar';
import TaskMasterPanel from './main-content/TaskMasterPanel';
import type { MainContentProps } from './main-content/types';
import MainContentHeader from './subcomponents/MainContentHeader';
import MainContentStateView from './subcomponents/MainContentStateView';
import EditorSidebar from './subcomponents/EditorSidebar';
import TaskMasterPanel from './subcomponents/TaskMasterPanel';
import type { MainContentProps } from '../types/types';
import { useTaskMaster } from '../contexts/TaskMasterContext';
import { useTasksSettings } from '../contexts/TasksSettingsContext';
import { useUiPreferences } from '../hooks/useUiPreferences';
import { useEditorSidebar } from '../hooks/main-content/useEditorSidebar';
import type { Project } from '../types/app';
import { useTaskMaster } from '../../../contexts/TaskMasterContext';
import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
import { useUiPreferences } from '../../../hooks/useUiPreferences';
import { useEditorSidebar } from '../hooks/useEditorSidebar';
import type { Project } from '../../../types/app';
const AnyStandaloneShell = StandaloneShell as any;
const AnyGitPanel = GitPanel as any;

View File

@@ -1,5 +1,5 @@
import CodeEditor from '../CodeEditor';
import type { EditorSidebarProps } from './types';
import CodeEditor from '../../../CodeEditor';
import type { EditorSidebarProps } from '../../types/types';
const AnyCodeEditor = CodeEditor as any;

View File

@@ -1,7 +1,7 @@
import MobileMenuButton from './MobileMenuButton';
import MainContentTabSwitcher from './MainContentTabSwitcher';
import MainContentTitle from './MainContentTitle';
import type { MainContentHeaderProps } from './types';
import type { MainContentHeaderProps } from '../../types/types';
export default function MainContentHeader({
activeTab,

View File

@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import MobileMenuButton from './MobileMenuButton';
import type { MainContentStateViewProps } from './types';
import type { MainContentStateViewProps } from '../../types/types';
export default function MainContentStateView({ mode, isMobile, onMenuClick }: MainContentStateViewProps) {
const { t } = useTranslation();

View File

@@ -1,5 +1,5 @@
import Tooltip from '../Tooltip';
import type { AppTab } from '../../types/app';
import Tooltip from '../../../Tooltip';
import type { AppTab } from '../../../../types/app';
import type { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';

View File

@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import SessionProviderLogo from '../SessionProviderLogo';
import type { AppTab, Project, ProjectSession } from '../../types/app';
import SessionProviderLogo from '../../../SessionProviderLogo';
import type { AppTab, Project, ProjectSession } from '../../../../types/app';
type MainContentTitleProps = {
activeTab: AppTab;

View File

@@ -1,5 +1,5 @@
import type { MobileMenuButtonProps } from './types';
import { useMobileMenuHandlers } from '../../hooks/main-content/useMobileMenuHandlers';
import type { MobileMenuButtonProps } from '../../types/types';
import { useMobileMenuHandlers } from '../../hooks/useMobileMenuHandlers';
export default function MobileMenuButton({ onMenuClick, compact = false }: MobileMenuButtonProps) {
const { handleMobileMenuClick, handleMobileMenuTouchEnd } = useMobileMenuHandlers(onMenuClick);

View File

@@ -1,11 +1,11 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import TaskList from '../TaskList';
import TaskDetail from '../TaskDetail';
import PRDEditor from '../PRDEditor';
import { useTaskMaster } from '../../contexts/TaskMasterContext';
import { api } from '../../utils/api';
import type { Project } from '../../types/app';
import type { PrdFile, TaskMasterPanelProps, TaskMasterTask, TaskSelection } from './types';
import TaskList from '../../../TaskList';
import TaskDetail from '../../../TaskDetail';
import PRDEditor from '../../../PRDEditor';
import { useTaskMaster } from '../../../../contexts/TaskMasterContext';
import { api } from '../../../../utils/api';
import type { Project } from '../../../../types/app';
import type { PrdFile, TaskMasterPanelProps, TaskMasterTask, TaskSelection } from '../../types/types';
const AnyTaskList = TaskList as any;
const AnyTaskDetail = TaskDetail as any;

View File

@@ -1,8 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import type React from 'react';
import type { TFunction } from 'i18next';
import { api } from '../utils/api';
import type { Project, ProjectSession } from '../types/app';
import { api } from '../../../utils/api';
import type { Project, ProjectSession } from '../../../types/app';
import type {
AdditionalSessionsByProject,
DeleteProjectConfirmation,
@@ -10,7 +10,7 @@ import type {
ProjectSortOrder,
SessionDeleteConfirmation,
SessionWithProvider,
} from '../components/sidebar/types';
} from '../types/types';
import {
filterProjects,
getAllSessions,
@@ -18,7 +18,7 @@ import {
persistStarredProjects,
readProjectSortOrder,
sortProjects,
} from '../components/sidebar/utils';
} from '../utils/utils';
type UseSidebarControllerArgs = {
projects: Project[];

View File

@@ -1,11 +1,11 @@
import type { TFunction } from 'i18next';
import type { Project } from '../../types/app';
import type { Project } from '../../../types/app';
import type {
AdditionalSessionsByProject,
ProjectSortOrder,
SessionViewModel,
SessionWithProvider,
} from './types';
} from '../types/types';
export const readProjectSortOrder = (): ProjectSortOrder => {
try {

View File

@@ -1,17 +1,17 @@
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useDeviceSettings } from '../hooks/useDeviceSettings';
import { useVersionCheck } from '../hooks/useVersionCheck';
import { useUiPreferences } from '../hooks/useUiPreferences';
import { useDeviceSettings } from '../../../hooks/useDeviceSettings';
import { useVersionCheck } from '../../../hooks/useVersionCheck';
import { useUiPreferences } from '../../../hooks/useUiPreferences';
import { useSidebarController } from '../hooks/useSidebarController';
import { useTaskMaster } from '../contexts/TaskMasterContext';
import { useTasksSettings } from '../contexts/TasksSettingsContext';
import SidebarCollapsed from './sidebar/SidebarCollapsed';
import SidebarContent from './sidebar/SidebarContent';
import SidebarModals from './sidebar/SidebarModals';
import type { Project } from '../types/app';
import type { SidebarProjectListProps } from './sidebar/SidebarProjectList';
import type { MCPServerStatus, SidebarProps } from './sidebar/types';
import { useTaskMaster } from '../../../contexts/TaskMasterContext';
import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
import SidebarCollapsed from './subcomponents/SidebarCollapsed';
import SidebarContent from './subcomponents/SidebarContent';
import SidebarModals from './subcomponents/SidebarModals';
import type { Project } from '../../../types/app';
import type { SidebarProjectListProps } from './subcomponents/SidebarProjectList';
import type { MCPServerStatus, SidebarProps } from '../types/types';
type TaskMasterSidebarContext = {
setCurrentProject: (project: Project) => void;

View File

@@ -1,7 +1,7 @@
import { ScrollArea } from '../ui/scroll-area';
import { ScrollArea } from '../../../ui/scroll-area';
import type { TFunction } from 'i18next';
import type { Project } from '../../types/app';
import type { ReleaseInfo } from '../../types/sharedTypes';
import type { Project } from '../../../../types/app';
import type { ReleaseInfo } from '../../../../types/sharedTypes';
import SidebarFooter from './SidebarFooter';
import SidebarHeader from './SidebarHeader';
import SidebarProjectList, { type SidebarProjectListProps } from './SidebarProjectList';

View File

@@ -1,7 +1,7 @@
import { Settings } from 'lucide-react';
import type { TFunction } from 'i18next';
import type { ReleaseInfo } from '../../types/sharedTypes';
import { Button } from '../ui/button';
import type { ReleaseInfo } from '../../../../types/sharedTypes';
import { Button } from '../../../ui/button';
type SidebarFooterProps = {
updateAvailable: boolean;

View File

@@ -1,8 +1,8 @@
import { FolderPlus, MessageSquare, RefreshCw, Search, X } from 'lucide-react';
import type { TFunction } from 'i18next';
import { Button } from '../ui/button';
import { Input } from '../ui/input';
import { IS_PLATFORM } from '../../constants/config';
import { Button } from '../../../ui/button';
import { Input } from '../../../ui/input';
import { IS_PLATFORM } from '../../../../constants/config';
type SidebarHeaderProps = {
isPWA: boolean;

View File

@@ -1,13 +1,13 @@
import ReactDOM from 'react-dom';
import { AlertTriangle, Trash2 } from 'lucide-react';
import type { TFunction } from 'i18next';
import { Button } from '../ui/button';
import ProjectCreationWizard from '../ProjectCreationWizard';
import Settings from '../Settings';
import VersionUpgradeModal from '../modals/VersionUpgradeModal';
import type { Project } from '../../types/app';
import type { ReleaseInfo } from '../../types/sharedTypes';
import type { DeleteProjectConfirmation, SessionDeleteConfirmation } from './types';
import { Button } from '../../../ui/button';
import ProjectCreationWizard from '../../../ProjectCreationWizard';
import Settings from '../../../Settings';
import VersionUpgradeModal from '../../../modals/VersionUpgradeModal';
import type { Project } from '../../../../types/app';
import type { ReleaseInfo } from '../../../../types/sharedTypes';
import type { DeleteProjectConfirmation, SessionDeleteConfirmation } from '../../types/types';
type SidebarModalsProps = {
projects: Project[];

View File

@@ -1,11 +1,11 @@
import { Button } from '../ui/button';
import { Button } from '../../../ui/button';
import { Check, ChevronDown, ChevronRight, Edit3, Folder, FolderOpen, Star, Trash2, X } from 'lucide-react';
import type { TFunction } from 'i18next';
import { cn } from '../../lib/utils';
import TaskIndicator from '../TaskIndicator';
import type { Project, ProjectSession, SessionProvider } from '../../types/app';
import type { MCPServerStatus, SessionWithProvider, TouchHandlerFactory } from './types';
import { getTaskIndicatorStatus } from './utils';
import { cn } from '../../../../lib/utils';
import TaskIndicator from '../../../TaskIndicator';
import type { Project, ProjectSession, SessionProvider } from '../../../../types/app';
import type { MCPServerStatus, SessionWithProvider, TouchHandlerFactory } from '../../types/types';
import { getTaskIndicatorStatus } from '../../utils/utils';
import SidebarProjectSessions from './SidebarProjectSessions';
type SidebarProjectItemProps = {

View File

@@ -1,11 +1,11 @@
import type { TFunction } from 'i18next';
import type { LoadingProgress, Project, ProjectSession, SessionProvider } from '../../types/app';
import type { LoadingProgress, Project, ProjectSession, SessionProvider } from '../../../../types/app';
import type {
LoadingSessionsByProject,
MCPServerStatus,
SessionWithProvider,
TouchHandlerFactory,
} from './types';
} from '../../types/types';
import SidebarProjectItem from './SidebarProjectItem';
import SidebarProjectsState from './SidebarProjectsState';

View File

@@ -1,8 +1,8 @@
import { ChevronDown, Plus } from 'lucide-react';
import type { TFunction } from 'i18next';
import { Button } from '../ui/button';
import type { Project, ProjectSession, SessionProvider } from '../../types/app';
import type { SessionWithProvider, TouchHandlerFactory } from './types';
import { Button } from '../../../ui/button';
import type { Project, ProjectSession, SessionProvider } from '../../../../types/app';
import type { SessionWithProvider, TouchHandlerFactory } from '../../types/types';
import SidebarSessionItem from './SidebarSessionItem';
type SidebarProjectSessionsProps = {

View File

@@ -1,6 +1,6 @@
import { Folder, Search } from 'lucide-react';
import type { TFunction } from 'i18next';
import type { LoadingProgress } from '../../types/app';
import type { LoadingProgress } from '../../../../types/app';
type SidebarProjectsStateProps = {
isLoading: boolean;

View File

@@ -1,13 +1,13 @@
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
import { Badge } from '../../../ui/badge';
import { Button } from '../../../ui/button';
import { Check, Clock, Edit2, Trash2, X } from 'lucide-react';
import type { TFunction } from 'i18next';
import { cn } from '../../lib/utils';
import { formatTimeAgo } from '../../utils/dateUtils';
import type { Project, ProjectSession, SessionProvider } from '../../types/app';
import type { SessionWithProvider, TouchHandlerFactory } from './types';
import { createSessionViewModel } from './utils';
import SessionProviderLogo from '../SessionProviderLogo';
import { cn } from '../../../../lib/utils';
import { formatTimeAgo } from '../../../../utils/dateUtils';
import type { Project, ProjectSession, SessionProvider } from '../../../../types/app';
import type { SessionWithProvider, TouchHandlerFactory } from '../../types/types';
import { createSessionViewModel } from '../../utils/utils';
import SessionProviderLogo from '../../../SessionProviderLogo';
type SidebarSessionItemProps = {
project: Project;

View File

@@ -1,109 +0,0 @@
import { useState, useRef, useCallback } from 'react';
export function useAudioRecorder() {
const [isRecording, setRecording] = useState(false);
const [audioBlob, setAudioBlob] = useState(null);
const [error, setError] = useState(null);
const mediaRecorderRef = useRef(null);
const streamRef = useRef(null);
const chunksRef = useRef([]);
const start = useCallback(async () => {
try {
setError(null);
setAudioBlob(null);
chunksRef.current = [];
// Request microphone access
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 16000,
}
});
streamRef.current = stream;
// Determine supported MIME type
const mimeType = MediaRecorder.isTypeSupported('audio/webm')
? 'audio/webm'
: 'audio/mp4';
// Create media recorder
const recorder = new MediaRecorder(stream, { mimeType });
mediaRecorderRef.current = recorder;
// Set up event handlers
recorder.ondataavailable = (e) => {
if (e.data.size > 0) {
chunksRef.current.push(e.data);
}
};
recorder.onstop = () => {
// Create blob from chunks
const blob = new Blob(chunksRef.current, { type: mimeType });
setAudioBlob(blob);
// Clean up stream
if (streamRef.current) {
streamRef.current.getTracks().forEach(track => track.stop());
streamRef.current = null;
}
};
recorder.onerror = (event) => {
console.error('MediaRecorder error:', event);
setError('Recording failed');
setRecording(false);
};
// Start recording
recorder.start();
setRecording(true);
console.log('Recording started');
} catch (err) {
console.error('Failed to start recording:', err);
setError(err.message || 'Failed to start recording');
setRecording(false);
}
}, []);
const stop = useCallback(() => {
console.log('Stop called, recorder state:', mediaRecorderRef.current?.state);
try {
if (mediaRecorderRef.current && mediaRecorderRef.current.state === 'recording') {
mediaRecorderRef.current.stop();
console.log('Recording stopped');
}
} catch (err) {
console.error('Error stopping recorder:', err);
}
// Always update state
setRecording(false);
// Clean up stream if still active
if (streamRef.current) {
streamRef.current.getTracks().forEach(track => track.stop());
streamRef.current = null;
}
}, []);
const reset = useCallback(() => {
setAudioBlob(null);
setError(null);
chunksRef.current = [];
}, []);
return {
isRecording,
audioBlob,
error,
start,
stop,
reset
};
}