---
name: frontend-architecture
description: Frontend package architecture for ChainGraph visual flow editor. Use when working on apps/chaingraph-frontend, React components, Effector stores, XYFlow integration, UI components, providers, or hooks. Covers directory structure, domain organization, provider hierarchy, component patterns. Triggers: frontend, react, component, provider, hook, ui, sidebar, flow editor, chaingraph-frontend, vite, tailwind.
---

# ChainGraph Frontend Architecture

This skill provides architectural guidance for the `@badaitech/chaingraph-frontend` package - a React-based visual programming interface for designing computational graphs.

## Package Overview

**Location**: `apps/chaingraph-frontend/`
**Purpose**: Visual flow editor for ChainGraph using XYFlow
**Stack**: React 19 + Vite + TypeScript + Effector + XYFlow + Tailwind + Radix UI

## Directory Structure

```
apps/chaingraph-frontend/src/
├── main.tsx                 # Entry point, store initialization
├── App.tsx                  # Router setup
├── FlowLayout.tsx           # Layout: Sidebar + Flow
├── config.ts                # Environment configuration
│
├── components/
│   ├── flow/                # Main flow editor (see below)
│   ├── ui/                  # Radix UI components (31 files)
│   ├── sidebar/             # Navigation & flow list
│   ├── dnd/                 # Drag-and-drop (dnd-kit)
│   ├── debug/               # Debug tools
│   └── theme/               # Theme switching
│
├── store/                   # Effector state (15 domains)
│   ├── domains.ts           # Domain definitions
│   ├── initialization.ts    # App init orchestration
│   ├── nodes/               # Node CRUD, positions
│   ├── edges/               # Edge connections, anchors
│   ├── flow/                # Active flow, metadata
│   ├── ports-v2/            # Port values, configs, UI
│   ├── execution/           # Execution state
│   ├── trpc/                # tRPC clients (2 servers)
│   └── ...                  # 20+ more domains
│
├── hooks/                   # Global custom hooks
└── providers/               # Provider hierarchy
```

## Flow Component Structure

```
components/flow/
├── Flow.tsx                 # Main XYFlow container (13.6KB)
├── nodes/
│   ├── ChaingraphNode/      # Main node component (17 files)
│   │   ├── ChaingraphNodeOptimized.tsx  # Perf-optimized
│   │   └── ports/           # ArrayPort, ObjectPort
│   ├── AnchorNode/          # Edge anchor visualization
│   └── GroupNode/           # Group nodes
├── edges/
│   ├── components/          # Edge UI
│   └── utils/               # Edge calculations
└── hooks/                   # 18 flow-specific hooks
    ├── useNodeDragHandling.ts
    ├── useNodeSelection.ts
    ├── useFlowCopyPaste.ts
    ├── useKeyboardShortcuts.ts
    └── ...
```

## Key Architectural Patterns

### 1. Two tRPC Clients

The app manages TWO separate WebSocket connections:

```typescript
// Main Server (flows, nodes, edges)
$trpcClient → ws://localhost:3001

// Executor Server (execution events)
$trpcClientExecutor → ws://localhost:4021
```

### 2. Effector Domain-Based Stores

15 domains organized by feature:

| Domain | Purpose |
|--------|---------|
| `nodes` | Node CRUD, positions, dimensions |
| `edges` | Connections, anchors, selection |
| `flow` | Active flow, metadata |
| `ports-v2` | Port values, configs, UI state |
| `execution` | Execution state & events |
| `trpc` | tRPC client instances |
| `xyflow` | XYFlow render optimization |

### 3. Optimistic Updates + Debouncing

```typescript
// Pattern: Local update → Debounce → Server sync
updateNodePositionLocal(event)  // Immediate UI update
// After NODE_POSITION_DEBOUNCE_MS (300ms)...
updateNodePositionFx(event)     // Send to server
```

### 4. Provider Hierarchy

```
ChainGraphProvider
  └─ RootProvider
      ├─ ThemeProvider
      ├─ ReactFlowProvider
      │   ├─ ZoomProvider
      │   ├─ DndContextProvider
      │   └─ MenuPositionProvider
      └─ [children]
```

## Store Patterns

### Creating a Store

```typescript
import { nodesDomain } from '../domains'

// Events (commands)
export const addNode = nodesDomain.createEvent<INode>()
export const removeNode = nodesDomain.createEvent<string>()

// Effects (async operations)
export const addNodeFx = nodesDomain.createEffect(async (params) => {
  const client = $trpcClient.getState()
  return client.flow.addNode.mutate(params)
})

// Stores
export const $nodes = nodesDomain.createStore<Record<string, INode>>({})
  .on(addNode, (nodes, node) => ({ ...nodes, [node.id]: node }))
  .on(removeNode, (nodes, id) => {
    const { [id]: _, ...rest } = nodes
    return rest
  })

// Derived stores
export const $selectedNodeIds = combine($nodes, (nodes) =>
  Object.keys(nodes).filter(id => nodes[id].isSelected)
)
```

### Global Reset Pattern

All stores support global reset:

```typescript
import { globalReset } from '../global'

$myStore.reset(globalReset)
```

## Component Patterns

### Using Effector in Components

```typescript
import { useUnit } from 'effector-react'
import { $nodes, addNode } from '@/store/nodes'

function MyComponent() {
  const [nodes, handleAddNode] = useUnit([$nodes, addNode])
  // ...
}
```

### Lazy Loading

```typescript
const LazyComponent = lazy(() => import('./HeavyComponent'))

// In render
<Suspense fallback={<Loading />}>
  <LazyComponent />
</Suspense>
```

## Performance Considerations

1. **Node Position Interpolation** - Smooth animations via `position-interpolation-advanced.ts`
2. **XYFlow Store** - Combined render data per node to reduce re-renders
3. **Effector Sampling** - Accumulate rapid updates, sample at intervals
4. **Debounce Constants**:
   - `NODE_POSITION_DEBOUNCE_MS = 300`
   - `NODE_DIMENSIONS_DEBOUNCE_MS = 500`
   - `NODE_UI_DEBOUNCE_MS = 1000`

## Key Files Reference

| File | Purpose |
|------|---------|
| `src/main.tsx` | App bootstrap |
| `src/store/initialization.ts` | Init orchestration |
| `src/store/domains.ts` | 13 Effector domains |
| `src/components/flow/Flow.tsx` | Main flow component |
| `src/providers/ChainGraphProvider.tsx` | Top-level provider |

## Best Practices

1. **Use domain-based stores** - Create stores within appropriate domains
2. **Optimistic updates first** - Update local store immediately, sync to server with debounce
3. **Avoid direct tRPC calls in components** - Use Effector effects
4. **Use `useUnit`** - Not `useStore` or `useEvent` separately
5. **Reset on `globalReset`** - All stores should support global reset
6. **Lazy load heavy components** - Especially documentation/tooltips
7. **Use ports-v2** - Legacy `ports/` is being phased out

## Related Skills

- `effector-patterns` - Effector state management anti-patterns
- `xyflow-patterns` - XYFlow integration patterns
- `subscription-sync` - Real-time data synchronization
- `optimistic-updates` - Optimistic UI patterns
- `chaingraph-concepts` - Core domain concepts
- `trpc-patterns` - tRPC framework patterns
- `trpc-flow-editing` - Flow editing tRPC procedures
