* Update cursor rules with specific backend, frontend, docs * Update image example Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * update image example Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Refactor frontend development guidelines: streamline sections, remove outdated icon development instructions, and update checklist for clarity and consistency. --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
374 lines
8.3 KiB
Text
374 lines
8.3 KiB
Text
---
|
|
description: "Guidelines for frontend development in Langflow, focusing on React/TypeScript UI components, build processes, and frontend testing."
|
|
globs:
|
|
- "src/frontend/**/*.{ts,tsx,js,jsx}"
|
|
- "src/frontend/**/*.{css,scss,json}"
|
|
- "src/frontend/package*.json"
|
|
- "src/frontend/vite.config.*"
|
|
- "src/frontend/tailwind.config.*"
|
|
- "src/frontend/tsconfig.json"
|
|
alwaysApply: false
|
|
---
|
|
|
|
## 1. Frontend Environment Setup
|
|
|
|
### Prerequisites
|
|
- **Node.js:** v22.12 LTS for JavaScript runtime
|
|
- **Package Manager:** npm (v10.9) for dependency management
|
|
- **Development Tools:** Vite for build tooling
|
|
|
|
### Frontend Service
|
|
```bash
|
|
make frontend # Start Vite dev server on port 3000
|
|
```
|
|
- Hot-reload enabled for UI changes
|
|
- Access at: http://localhost:3000/
|
|
- Frontend source: `src/frontend/`
|
|
|
|
---
|
|
|
|
## 2. Frontend Structure
|
|
|
|
### Directory Layout
|
|
```
|
|
src/frontend/src/
|
|
├── components/ # Reusable UI components
|
|
├── pages/ # Page-level components
|
|
├── icons/ # Component icons and lazy loading
|
|
├── stores/ # State management (Zustand)
|
|
├── types/ # TypeScript type definitions
|
|
├── utils/ # Utility functions
|
|
├── hooks/ # Custom React hooks
|
|
├── services/ # API service functions
|
|
└── assets/ # Static assets
|
|
```
|
|
|
|
### Key Technologies
|
|
- **React 18** with TypeScript
|
|
- **Vite** for build tooling and dev server
|
|
- **Tailwind CSS** for styling
|
|
- **Zustand** for state management
|
|
- **React Flow** for flow graph visualization
|
|
- **Lucide React** for icons
|
|
|
|
---
|
|
|
|
## 3. Frontend Code Quality
|
|
|
|
### Formatting
|
|
```bash
|
|
make format_frontend # Format TypeScript/JavaScript code
|
|
```
|
|
|
|
### Linting
|
|
```bash
|
|
make lint # Run ESLint and TypeScript checks
|
|
```
|
|
|
|
### Testing
|
|
```bash
|
|
make tests_frontend # Run frontend tests (requires additional setup)
|
|
```
|
|
|
|
### Pre-commit Workflow
|
|
1. Run `make format_frontend`
|
|
2. Run `make lint`
|
|
3. Test changes in browser
|
|
4. Commit changes
|
|
|
|
---
|
|
|
|
## 4. State Management
|
|
|
|
### Zustand Stores
|
|
```typescript
|
|
// stores/myStore.ts
|
|
import { create } from 'zustand';
|
|
|
|
interface MyState {
|
|
value: string;
|
|
setValue: (value: string) => void;
|
|
}
|
|
|
|
export const useMyStore = create<MyState>((set) => ({
|
|
value: '',
|
|
setValue: (value) => set({ value }),
|
|
}));
|
|
```
|
|
|
|
### Using Stores in Components
|
|
```typescript
|
|
// components/MyComponent.tsx
|
|
import { useMyStore } from '@/stores/myStore';
|
|
|
|
export function MyComponent() {
|
|
const { value, setValue } = useMyStore();
|
|
|
|
return (
|
|
<input
|
|
value={value}
|
|
onChange={(e) => setValue(e.target.value)}
|
|
/>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. API Integration
|
|
|
|
### Service Functions
|
|
```typescript
|
|
// services/api.ts
|
|
import { api } from '@/controllers/API';
|
|
|
|
export async function createFlow(flowData: FlowData) {
|
|
const response = await api.post('/flows/', flowData);
|
|
return response.data;
|
|
}
|
|
|
|
export async function getFlows() {
|
|
const response = await api.get('/flows/');
|
|
return response.data;
|
|
}
|
|
```
|
|
|
|
### Error Handling
|
|
```typescript
|
|
// hooks/useApi.ts
|
|
import { useState, useCallback } from 'react';
|
|
|
|
export function useApi<T>(apiFunction: (...args: any[]) => Promise<T>) {
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const execute = useCallback(async (...args: any[]) => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
const result = await apiFunction(...args);
|
|
return result;
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
throw err;
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [apiFunction]);
|
|
|
|
return { execute, loading, error };
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. React Flow Integration
|
|
|
|
### Flow Graph Components
|
|
```typescript
|
|
// components/FlowGraph.tsx
|
|
import ReactFlow, {
|
|
Node,
|
|
Edge,
|
|
Controls,
|
|
Background
|
|
} from 'reactflow';
|
|
|
|
interface FlowGraphProps {
|
|
nodes: Node[];
|
|
edges: Edge[];
|
|
onNodesChange: (changes: NodeChange[]) => void;
|
|
onEdgesChange: (changes: EdgeChange[]) => void;
|
|
}
|
|
|
|
export function FlowGraph({
|
|
nodes,
|
|
edges,
|
|
onNodesChange,
|
|
onEdgesChange
|
|
}: FlowGraphProps) {
|
|
return (
|
|
<div className="w-full h-full">
|
|
<ReactFlow
|
|
nodes={nodes}
|
|
edges={edges}
|
|
onNodesChange={onNodesChange}
|
|
onEdgesChange={onEdgesChange}
|
|
fitView
|
|
>
|
|
<Controls />
|
|
<Background />
|
|
</ReactFlow>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Custom Node Types
|
|
```typescript
|
|
// components/nodes/ComponentNode.tsx
|
|
import { memo } from 'react';
|
|
import { Handle, Position } from 'reactflow';
|
|
|
|
interface ComponentNodeProps {
|
|
data: {
|
|
label: string;
|
|
icon?: string;
|
|
};
|
|
}
|
|
|
|
export const ComponentNode = memo(({ data }: ComponentNodeProps) => {
|
|
return (
|
|
<div className="px-4 py-2 shadow-md rounded-md bg-white border">
|
|
<Handle type="target" position={Position.Top} />
|
|
|
|
<div className="flex items-center">
|
|
{data.icon && (
|
|
<img src={data.icon} alt="" className="w-4 h-4 mr-2" />
|
|
)}
|
|
<span className="text-sm">{data.label}</span>
|
|
</div>
|
|
|
|
<Handle type="source" position={Position.Bottom} />
|
|
</div>
|
|
);
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Styling with Tailwind
|
|
|
|
### Component Styling
|
|
```typescript
|
|
// components/Button.tsx
|
|
import { cn } from '@/utils/cn';
|
|
|
|
interface ButtonProps {
|
|
variant?: 'primary' | 'secondary';
|
|
size?: 'sm' | 'md' | 'lg';
|
|
children: React.ReactNode;
|
|
onClick?: () => void;
|
|
}
|
|
|
|
export function Button({
|
|
variant = 'primary',
|
|
size = 'md',
|
|
children,
|
|
onClick
|
|
}: ButtonProps) {
|
|
return (
|
|
<button
|
|
className={cn(
|
|
'rounded-md font-medium transition-colors',
|
|
{
|
|
'bg-blue-600 hover:bg-blue-700 text-white': variant === 'primary',
|
|
'bg-gray-200 hover:bg-gray-300 text-gray-900': variant === 'secondary',
|
|
'px-2 py-1 text-sm': size === 'sm',
|
|
'px-4 py-2 text-base': size === 'md',
|
|
'px-6 py-3 text-lg': size === 'lg',
|
|
}
|
|
)}
|
|
onClick={onClick}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Dark Mode Support
|
|
```typescript
|
|
// hooks/useDarkMode.ts
|
|
import { useDarkStore } from '@/stores/darkStore';
|
|
|
|
export function useDarkMode() {
|
|
const { dark, setDark } = useDarkStore();
|
|
|
|
const toggle = () => setDark(!dark);
|
|
|
|
return { isDark: dark, toggle };
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Frontend Testing
|
|
|
|
### Component Testing
|
|
```typescript
|
|
// __tests__/Button.test.tsx
|
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
import { Button } from '@/components/Button';
|
|
|
|
describe('Button', () => {
|
|
it('renders with correct text', () => {
|
|
render(<Button>Click me</Button>);
|
|
expect(screen.getByText('Click me')).toBeInTheDocument();
|
|
});
|
|
|
|
it('calls onClick when clicked', () => {
|
|
const handleClick = jest.fn();
|
|
render(<Button onClick={handleClick}>Click me</Button>);
|
|
|
|
fireEvent.click(screen.getByText('Click me'));
|
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
```
|
|
|
|
### Integration Testing
|
|
```typescript
|
|
// __tests__/FlowEditor.test.tsx
|
|
import { render, screen } from '@testing-library/react';
|
|
import { FlowEditor } from '@/pages/FlowEditor';
|
|
|
|
describe('FlowEditor', () => {
|
|
it('loads flow data correctly', async () => {
|
|
render(<FlowEditor flowId="test-flow-id" />);
|
|
|
|
// Wait for flow to load
|
|
await screen.findByText('Flow loaded');
|
|
|
|
expect(screen.getByTestId('flow-canvas')).toBeInTheDocument();
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Build and Deployment
|
|
|
|
### Development Build
|
|
```bash
|
|
make build_frontend # Build frontend static files
|
|
```
|
|
|
|
### Production Build
|
|
```bash
|
|
cd src/frontend
|
|
npm run build # Creates dist/ directory
|
|
```
|
|
|
|
### Build Integration
|
|
- Frontend builds to `src/frontend/dist/`
|
|
- Backend serves static files from this directory in production
|
|
- `make init` includes frontend build step
|
|
---
|
|
|
|
## Frontend Development Checklist
|
|
- [ ] Frontend service running with `make frontend`
|
|
- [ ] Changes hot-reload correctly in browser
|
|
- [ ] State management uses Zustand stores
|
|
- [ ] API calls use proper error handling
|
|
- [ ] Components styled with Tailwind CSS
|
|
- [ ] Dark mode support implemented where needed
|
|
- [ ] Code formatted with `make format_frontend`
|
|
- [ ] Linting passed with `make lint`
|
|
- [ ] Changes tested in both light and dark mode
|
|
|
|
- [ ] Components styled with Tailwind CSS
|
|
- [ ] Dark mode support implemented where needed
|
|
- [ ] Code formatted with `make format_frontend`
|
|
- [ ] Changes tested in both light and dark mode
|
|
|