Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • Wegent Frontend Architecture Analysis

Wegent Frontend Architecture Analysis

Project: Wegent AI - AI Agent Management Platform
Tech Stack: Next.js 15 + React 19 + TypeScript + Tailwind CSS + shadcn/ui
Analysis Date: 2026-02-01


Table of Contents

  1. Overview
  2. Next.js 15 App Router Structure
  3. Component Organization
  4. State Management Architecture
  5. Responsive Design System
  6. API Client Layer
  7. Internationalization (i18n)
  8. Chat Message Data Flow
  9. Key Design Patterns

Overview

The Wegent frontend is a Next.js 15 application built with modern React patterns. It uses a mobile-first responsive architecture with separate mobile/desktop page implementations for optimal UX. The architecture emphasizes:

  • High cohesion, low coupling: Each module has single responsibility
  • Context-based state management: React Context for global state
  • WebSocket-driven real-time updates: Socket.IO for streaming chat
  • Component-level responsive separation: Mobile vs Desktop components
  • Namespace-based i18n: Feature-organized translations

Next.js 15 App Router Structure

Directory Layout

src/app/
├── layout.tsx                    # Root layout with providers
├── page.tsx                      # Entry point (redirects to chat/code)
├── globals.css                   # Global styles
├── (tasks)/                      # Route group for task pages
│   ├── layout.tsx               # Shared layout for chat/code
│   ├── chat/
│   │   ├── page.tsx             # Router component (mobile/desktop)
│   │   ├── ChatPageDesktop.tsx  # Desktop implementation
│   │   └── ChatPageMobile.tsx   # Mobile implementation
│   ├── code/
│   │   ├── page.tsx
│   │   ├── CodePageDesktop.tsx
│   │   └── CodePageMobile.tsx
│   └── knowledge/               # Knowledge base pages
├── api/                         # Next.js API routes
│   ├── chat/
│   │   ├── stream/route.ts
│   │   ├── cancel/route.ts
│   │   └── ...
│   └── [...path]/route.ts       # Proxy to backend
├── login/                       # Authentication pages
├── admin/                       # Admin dashboard
└── shared/task/                 # Public task sharing page

Page Hierarchy Diagram

Route Groups

The (tasks) route group wraps chat and code pages with shared providers:

// src/app/(tasks)/layout.tsx
export default function TasksLayout({ children }: { children: React.ReactNode }) {
  return (
    <UserProvider>
      <SocketProvider>
        <DeviceProvider>
          <PetProvider>
            <TaskContextProvider>
              <ChatStreamProvider>
                {children}
                <PetStreamingBridge />
                <PetWidget />
                <GlobalAdminSetupWizard />
              </ChatStreamProvider>
            </TaskContextProvider>
          </PetProvider>
        </DeviceProvider>
      </SocketProvider>
    </UserProvider>
  )
}

This prevents task list reloading when switching between chat and code pages.


Component Organization

Directory Structure

src/
├── components/
│   ├── ui/                      # shadcn/ui base components
│   │   ├── button.tsx
│   │   ├── dialog.tsx
│   │   ├── input.tsx
│   │   └── ...
│   ├── common/                  # Shared common components
│   │   ├── ErrorBoundary.tsx
│   │   └── LoadingSpinner.tsx
│   └── I18nProvider.tsx         # i18n setup
│
├── features/                    # Feature-based modules
│   ├── tasks/
│   │   ├── components/
│   │   │   ├── sidebar/         # Task sidebar components
│   │   │   ├── message/         # Message display components
│   │   │   ├── input/           # Chat input components
│   │   │   ├── chat/            # Chat area components
│   │   │   ├── selector/        # Team/Repo selectors
│   │   │   └── group-chat/      # Group chat components
│   │   ├── contexts/            # Feature contexts
│   │   ├── hooks/               # Feature hooks
│   │   ├── service/             # Business logic services
│   │   └── state/               # State machines
│   ├── layout/                  # Layout components
│   ├── theme/                   # Theme management
│   └── common/                  # Common feature utilities
│
└── hooks/                       # Global custom hooks
    ├── useTranslation.ts
    ├── useMediaQuery.ts
    └── ...

Component Hierarchy (Chat Page)


State Management Architecture

Context Provider Stack

TaskContext Structure

interface TaskContextType {
  // Task Lists
  tasks: Task[]
  groupTasks: Task[]
  personalTasks: Task[]
  
  // Selection State
  selectedTask: Task | null
  selectedTaskDetail: TaskDetail | null
  setSelectedTask: (task: Task | null) => void
  
  // Data Operations
  refreshTasks: () => void
  refreshSelectedTaskDetail: (isAutoRefresh?: boolean) => void
  
  // Pagination
  loadMore: () => void
  hasMore: boolean
  loadingMore: boolean
  
  // Search
  searchTerm: string
  setSearchTerm: (term: string) => void
  searchTasks: (term: string) => Promise<void>
  
  // View Status
  markTaskAsViewed: (taskId: number, status: TaskStatus) => void
  getUnreadCount: (tasks: Task[]) => number
  viewStatusVersion: number
}

ChatStreamContext Flow


Responsive Design System

Breakpoint System

BreakpointScreen SizeUsage
Mobile≤767pxTouch-optimized UI with drawer sidebar
Tablet768px-1023pxUses desktop layout
Desktop≥1024pxFull-featured UI with resizable sidebar

Media Query Hooks

// src/features/layout/hooks/useMediaQuery.ts
export function useIsMobile(): boolean {
  return useMediaQuery('(max-width: 767px)')
}

export function useIsTablet(): boolean {
  return useMediaQuery('(min-width: 768px) and (max-width: 1023px)')
}

export function useIsDesktop(): boolean {
  return useMediaQuery('(min-width: 1024px)')
}

Responsive Component Organization

Dynamic Import Pattern

// src/app/(tasks)/chat/page.tsx
const ChatPageDesktop = dynamic(
  () => import('./ChatPageDesktop').then(mod => ({ default: mod.ChatPageDesktop })),
  { ssr: false }
)

const ChatPageMobile = dynamic(
  () => import('./ChatPageMobile').then(mod => ({ default: mod.ChatPageMobile })),
  { ssr: false }
)

export default function ChatPage() {
  const isMobile = useIsMobile()
  return isMobile ? <ChatPageMobile /> : <ChatPageDesktop />
}

Touch-Friendly Requirements

All interactive elements must be at least 44px × 44px:

// Mobile button styling
<Button className="h-11 min-w-[44px] px-4">
  {icon}
</Button>

When to Separate Components

ScenarioSolution
Layout differences >30%Create separate Mobile/Desktop components
Different interaction patternsSeparate for better UX
Performance optimizationUse dynamic imports with code splitting
Simple styling adjustmentsUse Tailwind responsive classes

API Client Layer

Directory Structure

src/apis/
├── client.ts           # Base HTTP client with interceptors
├── user.ts            # User authentication APIs
├── tasks.ts           # Task management APIs
├── subtasks.ts        # Subtask/chat message APIs
├── team.ts            # Team/Bot management APIs
├── chat.ts            # Chat streaming APIs
├── skills.ts          # Skill management APIs
├── knowledge.ts       # Knowledge base APIs
├── github.ts          # GitHub integration APIs
├── group-chat.ts      # Group chat APIs
└── mocks/             # MSW mock handlers (dev only)

Base Client Architecture

APIClient Implementation

// src/apis/client.ts
class APIClient {
  async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
    const url = `${this.getBaseURL()}${endpoint}`
    const token = getToken()

    const config: RequestInit = {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...(token && { Authorization: `Bearer ${token}` }),
        ...options.headers,
      },
    }

    const response = await fetch(url, config)
    
    // Handle 401 - Redirect to login
    if (response.status === 401) {
      removeToken()
      redirectToLogin()
    }

    if (!response.ok) {
      throw new ApiError(errorMsg, response.status)
    }

    return response.json()
  }
}

export const apiClient = new APIClient()

API Module Pattern

// src/apis/tasks.ts
export const taskApis = {
  getTasksLite: (params: PaginationParams): Promise<TaskListResponse> =>
    apiClient.get(`/tasks/lite?page=${params.page}&limit=${params.limit}`),

  getGroupTasksLite: (params: PaginationParams): Promise<TaskListResponse> =>
    apiClient.get(`/tasks/group/lite?page=${params.page}&limit=${params.limit}`),

  getPersonalTasksLite: (params: PaginationParams): Promise<TaskListResponse> =>
    apiClient.get(`/tasks/personal/lite?page=${params.page}&limit=${params.limit}`),

  createTask: (data: CreateTaskRequest): Promise<Task> =>
    apiClient.post('/tasks', data),

  updateTask: (id: number, data: UpdateTaskRequest): Promise<Task> =>
    apiClient.patch(`/tasks/${id}`, data),

  deleteTask: (id: number): Promise<void> =>
    apiClient.delete(`/tasks/${id}`),
}

WebSocket Integration


Internationalization (i18n)

Setup

// src/i18n/setup.ts
export const supportedLanguages = ['en', 'zh-CN']

export async function initI18n() {
  const resources = await loadTranslations()
  
  await i18next.use(initReactI18next).init({
    lng: process.env.I18N_LNG || 'en',
    fallbackLng: 'en',
    resources,
    defaultNS: 'common',
    ns: [
      'common',
      'chat',
      'settings',
      'tasks',
      'admin',
      'wizard',
      'groups',
      'knowledge',
      // ... 13 namespaces
    ],
  })
}

Namespace Structure

src/i18n/locales/
├── en/
│   ├── common.json       # Shared translations
│   ├── chat.json         # Chat feature
│   ├── tasks.json        # Task management
│   ├── settings.json     # User settings
│   ├── admin.json        # Admin dashboard
│   ├── wizard.json       # Setup wizard
│   ├── groups.json       # Group chat
│   └── ...
└── zh-CN/
    └── [same structure]

Usage Patterns

// ✅ CORRECT - Single namespace import
const { t } = useTranslation('groups')
t('title')                    // Current namespace

t('common:actions.save')      // Access other namespace
t('chat:export.no_messages')  // Cross-namespace access

// ❌ WRONG - Array with common first
const { t } = useTranslation(['common', 'groups']) // Breaks feature keys

Key Naming Convention

NamespaceUsage
commonShared UI elements (buttons, labels, errors)
chatChat interface, messages, streaming
tasksTask list, creation, management
settingsUser preferences, configuration
adminAdmin dashboard, system settings
groupsGroup chat, member management
knowledgeKnowledge base, documents

Chat Message Data Flow

CRITICAL: Single Source of Truth

┌─────────────────────────────────────────────────────────────────┐
│  ⚠️  ALWAYS use messages from useUnifiedMessages()              │
│                                                                │
│  selectedTaskDetail.subtasks = Backend cached data (stale)     │
│  messages (from hook) = Real-time data via WebSocket           │
│                                                                │
│  For display/export: Use messages                              │
│  For refresh/sync: Use selectedTaskDetail.subtasks             │
└─────────────────────────────────────────────────────────────────┘

Data Flow Architecture

Message Flow Sequence

useUnifiedMessages Hook

// src/features/tasks/hooks/useUnifiedMessages.ts
export function useUnifiedMessages({ 
  team, 
  isGroupChat 
}: UseUnifiedMessagesOptions): {
  messages: DisplayMessage[]
  isLoading: boolean
  error: Error | null
} {
  const { selectedTaskDetail } = useTaskContext()
  const { getTaskState } = useChatStreamContext()
  
  // Get real-time state from TaskStateManager
  const taskState = getTaskState(selectedTaskDetail?.id)
  
  // Merge backend data with real-time updates
  const messages = useMemo(() => {
    // 1. Start with backend subtasks
    const backendMessages = convertSubtasksToMessages(
      selectedTaskDetail?.subtasks
    )
    
    // 2. Merge with streamState.messages (WebSocket updates)
    const merged = mergeMessages(backendMessages, taskState?.messages)
    
    // 3. Sort and format for display
    return formatForDisplay(merged)
  }, [selectedTaskDetail?.subtasks, taskState?.messages])
  
  return { messages, isLoading, error }
}

Key Design Patterns

1. State Machine Pattern

TaskStateManager manages state for each task:

class TaskStateMachine {
  private state: TaskStateData
  private subscribers: Set<Listener>
  
  // Actions
  addUserMessage(content: string): void
  addAIMessage(): string  // Returns messageId
  appendContent(messageId: string, chunk: string): void
  markComplete(messageId: string): void
  markError(messageId: string, error: string): void
  
  // Recovery
  recover(): Promise<void>  // Resume interrupted streams
}

2. Service Pattern

Business logic extracted into services:

// src/features/tasks/service/teamService.ts
export const teamService = {
  useTeams: () => {
    // React Query or custom hook logic
    const [teams, setTeams] = useState<Team[]>([])
    const refreshTeams = async () => { /* ... */ }
    return { teams, refreshTeams }
  },
  
  useTeam: (id: number) => {
    // Single team operations
  }
}

3. Context Composition Pattern

Providers are composed hierarchically:

4. Component Separation Pattern

// Router component with dynamic imports
export default function ChatPage() {
  const isMobile = useIsMobile()
  
  // Dynamic imports for code splitting
  const ChatPageDesktop = dynamic(() => import('./ChatPageDesktop'))
  const ChatPageMobile = dynamic(() => import('./ChatPageMobile'))
  
  return isMobile ? <ChatPageMobile /> : <ChatPageDesktop />
}

// Desktop implementation
export function ChatPageDesktop() {
  // Desktop-specific logic and layout
  return (
    <ResizableSidebar>
      <TaskList />
    </ResizableSidebar>
    <ChatArea />
  )
}

// Mobile implementation  
export function ChatPageMobile() {
  // Mobile-specific logic and layout
  return (
    <DrawerSidebar>
      <TaskList />
    </DrawerSidebar>
    <MobileChatArea />
  )
}

5. Hook Pattern

// Feature-specific hook
export function useTaskOperations() {
  const { selectedTask, refreshTasks } = useTaskContext()
  const { sendMessage } = useChatStreamContext()
  
  const handleSend = async (content: string) => {
    await sendMessage({
      message: content,
      team_id: selectedTask?.team_id,
      task_id: selectedTask?.id,
    })
  }
  
  return { handleSend }
}

Complex UI/UX Logic Requiring Deep Analysis

1. Streaming Message Recovery

When WebSocket reconnects, the system must:

  • Detect interrupted streams
  • Resume from last received chunk
  • Handle temp-to-real task ID migration
  • Sync missed messages

File: src/features/tasks/state/TaskStateMachine.ts (lines 200-300)

2. Message Thinking/Tool Display

Complex parsing of thinking blocks:

  • Tool call extraction and rendering
  • Mixed content (thinking + code + results)
  • Step-by-step progress display
  • Error state visualization

Files:

  • src/features/tasks/components/message/thinking/ThinkingDisplay.tsx
  • src/features/tasks/components/message/thinking/utils/toolExtractor.ts

3. Responsive Sidebar Behavior

Desktop vs Mobile sidebar states:

  • Desktop: Resizable width, collapse state persisted to localStorage
  • Mobile: Drawer overlay, swipe to close, touch gestures

Files:

  • src/features/tasks/components/sidebar/ResizableSidebar.tsx
  • src/features/tasks/components/sidebar/TaskSidebar.tsx

4. Attachment & Context Management

Multi-file upload with context preservation:

  • File upload progress tracking
  • Attachment preview generation
  • Context items (knowledge bases, repos)
  • Cross-message context persistence

Files:

  • src/hooks/useMultiAttachment.ts
  • src/features/tasks/components/input/AttachmentPreview.tsx

5. Export Functionality

Message export with format selection:

  • Selectable message list
  • Markdown/HTML/JSON export formats
  • Attachment inclusion logic
  • Knowledge base reference handling

File: src/features/tasks/components/share/ExportSelectModal.tsx


Summary

Architecture Patterns

  1. Mobile-First Responsive: Separate Mobile/Desktop page implementations with dynamic imports
  2. Context-Based State: React Context for global state, no Redux/Zustand
  3. WebSocket-Driven: Real-time chat via Socket.IO with automatic recovery
  4. State Machine: TaskStateManager/TaskStateMachine for complex chat state
  5. Feature-Based Organization: Co-located components, hooks, and services
  6. Namespace i18n: Feature-organized translations for maintainability

Key Strengths

  • Clean separation of concerns: Features are self-contained
  • Optimized mobile UX: Touch-friendly, drawer-based navigation
  • Resilient streaming: Automatic recovery from connection issues
  • Type-safe: Full TypeScript coverage
  • Maintainable: Clear patterns, consistent naming

Areas Requiring Attention

  1. State synchronization: Complex flow between backend data and WebSocket updates
  2. Component splitting: Large components (>1000 lines) need further decomposition
  3. Test coverage: Ensure all state machine transitions are tested
  4. Performance: Virtualize long message lists for better performance

Document generated by AI analysis of Wegent frontend codebase