AgentSkillsCN

overleaf-frontend-patterns

Overleaf CE 代码库的前端开发模式。适用于在 Overleaf 编辑器中添加 UI 组件、在 services/web/modules/ 中创建新模块、以上下文与钩子实现 React 组件、使用主题变量编写 SCSS 样式,或在 Docker 中运行前端测试的场景。可通过关于 Overleaf 架构、导入路径(@/features、@/shared、@modules)、组件结构、CSS 变量、测试模式,或 Docker Webpack 工作流的查询进行触发。

SKILL.md
--- frontmatter
name: overleaf-frontend-patterns
description: Frontend development patterns for Overleaf CE codebase. Use when adding UI components to the Overleaf editor, creating new modules in services/web/modules/, implementing React components with contexts/hooks, writing SCSS styles with theme variables, or running frontend tests in Docker. Triggers on queries about Overleaf architecture, import paths (@/features, @/shared, @modules), component structure, CSS variables, testing patterns, or Docker webpack workflow.

Overleaf Frontend Development Patterns

Quick Architecture Overview

code
overleaf/
├── services/              # Microservices
│   └── web/              # Main app (focus here)
│       ├── app/src/      # Backend (Node.js/Express)
│       │   └── Features/ # 58 domain modules
│       ├── frontend/js/  # Frontend (React/TS)
│       │   ├── features/ # 45+ UI features
│       │   └── shared/   # Reusable components
│       └── modules/      # Custom plugins (your code!)
├── libraries/            # Shared packages
└── develop/              # Docker dev environment

Full architecture: See references/architecture.md

Directory Structure (services/web/)

code
services/web/
├── frontend/js/
│   ├── features/           # Feature modules (@/features)
│   ├── shared/             # Reusable components (@/shared)
│   └── infrastructure/     # Core utilities (@/infrastructure)
├── modules/                # External plugins (@modules - NO slash)
│   └── evidence-panel/     # Custom modules here
├── macros/                 # Webpack macros
└── locales/en.json         # i18n (alphabetically sorted)

Import Path Aliases

typescript
import X from '@/features/pdf-preview/...'      // Feature
import X from '@/shared/components/...'         // Shared
import X from '@/infrastructure/error-boundary' // Infrastructure
import X from '@modules/evidence-panel/...'     // Module (NO slash after @)
import getMeta from '@/utils/meta'              // Utilities

File Naming

TypePatternExample
Componentkebab-case.tsxpdf-logs-viewer.tsx
Hookuse-*.tsuse-compile-triggers.ts
Context*-context.tsxevidence-context.tsx
Test*.test.tsxevidence-panel.test.tsx

Quick Reference

Component Pattern

typescript
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import MaterialIcon from '@/shared/components/material-icon'
import withErrorBoundary from '@/infrastructure/error-boundary'

function MyComponent({ prop }: Props) {
  const { t } = useTranslation()
  return <div className="my-component">{t('key')}</div>
}

export default memo(withErrorBoundary(MyComponent, Fallback))

Context Pattern

typescript
const MyContext = createContext<Value | undefined>(undefined)

export function useMyContext() {
  const ctx = useContext(MyContext)
  if (!ctx) throw new Error('useMyContext must be used within MyProvider')
  return ctx
}

CSS Variables

scss
color: var(--content-primary);
background: var(--bg-light-secondary);
border: 1px solid var(--border-color);
color: var(--green-50);  // Accent

Development Workflow

NEVER run npm on host machine - Always use Docker.

bash
# Start dev server
cd overleaf/develop && bin/dev web webpack

# Restart webpack after new files
docker compose restart webpack

# Check compilation
docker compose logs webpack --tail 20 | grep -E "compiled|error"

# Run tests
docker exec develop-web-1 sh -c \
  "cd /overleaf/services/web && npm run test:frontend -- --grep 'MyFeature'"

Verification Loop (CRITICAL)

Webpack compile success ≠ Frontend working

  1. Webpack logs: Syntax/import errors
  2. Browser F12 Console: Runtime/React errors
  3. Visual test: UI renders correctly

New Editor Compatibility

typescript
import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils'
const newEditor = useIsNewEditorEnabled()
return newEditor ? <NewVariant /> : <OldVariant />

i18n

Add to locales/en.json (alphabetically sorted):

typescript
const { t } = useTranslation()
<span>{t('my_key')}</span>

Detailed References