# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository.

## Project Overview

This is the frontend web application for Suno, an AI music generation platform.
Built with Next.js, TypeScript, and modern React patterns, it provides the user
interface for music creation, editing, listening, and collaboration.

## Architecture & Key Technologies

### Core Stack

- **Next.js** with App Router - SSR, routing, and build system
- **React** with TypeScript - Component framework with strict typing
- **TanStack Query** - Data fetching, caching, and synchronization
- **MobX** - Mutable state management for complex application state. Should be
  used sparingly, preferring Tanstack Query and local state whenever possible.
  Exceptions can be made for entities that belong in global state.
- **Tailwind CSS** - Primary styling framework (new components outside of
  /studio & /edit2025)
- **Emotion Styled** - Required for new components in /create, /studio &
  /edit2025
- **Chakra UI** - Legacy styling system (older components, being phased out)

### UI & Component Architecture

- **Radix UI** - Unstyled, accessible component primitives
- **React Aria Components** - Additional accessibility primitives
- **Storybook** - Component development and documentation
- Component organization by feature/domain, not type

### Development & Quality Tools

- **Vitest** - Unit testing framework
- **Playwright** - End-to-end testing
- **ESLint + Prettier** - Code quality and formatting
- **TypeScript strict mode** - Enhanced type safety

## Common Development Commands

### Development Server

```bash
pnpm dev                    # Start Next.js dev server with Tailwind watch
pnpm build                  # Production build with DSP artifacts
pnpm start                  # Start production server
```

### Testing

```bash
pnpm test                   # Run Vitest unit tests
pnpm test-no-watch          # Run Vitest unit tests without watch
pnpm test --update          # Update test snapshots
pnpm test:e2e              # Run Playwright E2E tests
pnpm test:e2e-codegen      # Generate E2E tests interactively
```

### Code Quality

```bash
pnpm lint                   # ESLint linting
pnpm format:fix            # Prettier formatting
pnpm analyze               # Bundle size analysis
```

### Component Development

```bash
pnpm storybook             # Start Storybook dev server
pnpm build-storybook       # Build Storybook for deployment
```

### API Integration

```bash
pnpm gen-types             # Generate TypeScript client from Django OpenAPI schema
```

## Development Workflow

### Component Styling Strategy

- **New components outside /studio & /edit2025**: Use Tailwind CSS with design
  system tokens
- **New components in /studio & /edit2025**: Use Emotion Styled
- **Legacy components**: Chakra UI (🚫 NEVER use for new work)
- Use `twMerge` utility for Tailwind class combination

**Styling Guidelines:**

Use semantic design system tokens. If semantic tokens don't exist, fallback to
using standard tailwind styles.

For example, for colors these are the semantic tokens found in
`tailwind.colors.ts`. If there is a color that doesn't match, use the base
colors found later in the `tailwind.colors.ts` json.

### Example: Semantic Colors

- `text-foreground-primary`, `text-foreground-secondary`
- `bg-background-primary`, `bg-background-secondary`
- `border-border-primary`, `border-border-secondary`

### Example: Base Colors

- `text-strawberry-500`, `text-gray-50`
- `bg-pumpkin-100`, `bg-amethyst-900`
- `border-grass-200`, `border-gray-600`

```typescript
// ✅ Correct Tailwind usage
const className = twMerge(
  'flex items-center gap-2 p-4',
  'bg-background-primary text-foreground-primary',
  'border border-border-primary rounded-md',
  'hover:bg-background-secondary transition-colors',
  props.className
);
```

### State Management Patterns

**Priority Order:**

1. **TanStack Query** - All server state, API caching, background sync
2. **useState** - Local component state
3. **useContext** - For shared state across multiple components
4. **MobX** - Only for complex cross-component UI state (audio player, timeline)

**Critical Rules:**

- 🚫 NEVER add new MobX stores for server data - use TanStack Query instead
- 🚫 NEVER use `any` types - always provide proper TypeScript definitions
- 🚫 NEVER create complex useEffect chains for data fetching - use TanStack
  Query
- 🚫 NEVER access apiClient from a MobX store - use the `useApiClient` hook
  instead
- 🚫 NEVER use positional parameters for newly defined functions with 3+
  arguments - use named parameters instead
- ✅ ALWAYS use TanStack Query for ALL server state management
- ✅ ALWAYS implement proper TypeScript interfaces for all props
- ✅ ALWAYS handle loading, error, and success states consistently
- ✅ ALWAYS use named parameters (object destructuring) for newly defined
  functions with 3+ arguments

**Tanstack Query Example**

```typescript
// ✅ Preferred: TanStack Query for server state. These should be encapsulated in a feature hook and use query key factories for deeply nested keys

export const clipsKeys = {
  all: [{ scope: 'clips' }] as const,
  comments: ({
    clipId,
    sortBy,
  }: {
    clipId: string;
    sortBy?: CommentSortBy | null;
  }) => [{ ...clipsKeys.all[0], entity: 'comments', clipId, sortBy }] as const,
  replies: ({ clipId, commentId }: { clipId: string; commentId: string }) =>
    [{ ...clipsKeys.all[0], entity: 'replies', clipId, commentId }] as const,
};

const {
  data: clips,
  isLoading,
  error,
} = useQuery({
  queryKey: ['clips', userId],
  queryFn: async () => {
    const response = await apiClient.GET('/api/clips');
    if (!response.data) return undefined;
    return deepCamelKeys(response.data);
  },
  staleTime: 30000,
});

// ✅ Mutations with optimistic updates
const mutation = useMutation({
  mutationFn: (data) => apiClient.POST('/api/clips', { body: data }),
  onSuccess: () => queryClient.invalidateQueries(['clips']),
});
```

### API Integration

- Use generated TypeScript client from `pnpm gen-types`
- Backend must be running locally
- API client provides type-safe endpoints and models
- Handle loading, error, and success states consistently

**API Integration Example**

```typescript
// ✅ Use generated client from src/lib/gen.ts
import { useApiClient } from '@/lib/apiClient';
import { deepCamelKeys } from 'string-ts';

const apiClient = useApiClient();

// ✅ Error handling pattern
const { data, error, isLoading } = useQuery({
  queryKey: ['resource', id],
  queryFn: async () => {
    const response = await apiClient.GET('/api/resource/{id}', {
      params: { path: { id } },
    });
    if (!response.data) return undefined;
    return deepCamelKeys(response.data);
  },
  retry: (failureCount, error) => {
    if (error.status === 404) return false;
    return failureCount < 3;
  },
});

// Component error boundaries
if (error) {
  return <ErrorFallback error={error} retry={refetch} />;
}
// Loading states
if (isLoading) {
  return <SpinnerSVG />;
}
```

### Testing Strategy

- **Unit tests**: Components, hooks, utilities with Vitest
- **E2E tests**: Critical user flows with Playwright
- **Component tests**: Interactive development with Storybook
- **Snapshot tests**: For stable component rendering
- Add teardown steps to E2E tests to avoid data accumulation
- Run `pnpm format:fix` before committing work

### Analytics Logging

**Critical Rules:**

- ✅ ALWAYS import and use strongly-typed events from `/src/logging/eventTypes/`
- ✅ ALWAYS use `logWebUserEvent()` for all user behavior tracking
- ✅ ALWAYS Include transaction loggers for multi-step user flows
- ✅ ALWAYS Add context properties (clipId, source, userId) when available
- ✅ ALWAYS use camelCase for event names and properties
- 🚫 NEVER create arbitrary event names - use existing types from eventTypes
  from `/src/logging/eventTypes/`
- 🚫 NEVER log sensitive data (tokens, passwords, PII)
- 🚫 NEVER log analytics events without required context properties (eg, clipId,
  userId, source)
- 🚫 NEVER use snake_case for event names

#### Example: Basic Event Logging

```typescript
import logWebUserEvent from '@/logging/logWebUserEvent';

// ✅ Correct: Use typed events with camelCase names
logWebUserEvent({
  name: 'ClipPlayed', // this should be defined in a type file that is imported into @/logging/TrackingEventTypes.ts
  properties: {
    clipId: clip.id,
    playbackSource: 'feed',
  },
});

// ❌ Wrong: snake_case event names
logWebUserEvent({ name: 'clip_played' });

// ❌ Wrong: Arbitrary event names
logWebUserEvent({ name: 'userDidSomething' });
```

#### Example: Transaction Logging for Multi-Step Flows

```typescript
import { createTransactionLogger } from '@/logging/logWebUserEvent';

// ✅ Correct: Transaction logging with camelCase events
const txLogger = createTransactionLogger();
txLogger.logWebUserEvent({ name: 'CreateClipStarted' });
// ... user actions ...
txLogger.logWebUserEvent({
  name: 'CreateClipCompleted',
  properties: { clipId: newClip.id },
});
```

#### Example: Error Handling with Analytics

```typescript
// ✅ Correct: Combine analytics with error handling
try {
  const result = await apiCall();
  logWebUserEvent({ name: 'actionSucceeded', properties: { result } });
} catch (error) {
  logWebUserEvent({
    name: 'actionFailed',
    properties: { error: error.message, context: 'apiCall' },
  });
  console.error('API call failed:', error); // Sentry captures automatically
}
```

### Authentication & External Services

- **Clerk** - Social authentication (SaaS)
- **Tailscale** - Required for local development (run `tailscale funnel 8000`)
- **Statsig** - Feature flags and experiments
- **Stripe** - Payment processing
- **Sentry** - Error tracking and monitoring

## Code Organization Patterns

### Directory Structure

- `src/app/` - Next.js App Router pages and API routes
- `src/components/` - React components organized by feature
- `src/hooks/` - Custom React hooks
- `src/state/` - MobX stores and state management
- `src/lib/` - Utilities, API clients, and shared logic
- `src/utils/` - Pure utility functions
- `e2e/` - Playwright end-to-end tests

### Component Architecture

- Prefer composition over inheritance
- Use TypeScript interfaces for prop definitions
- Follow existing patterns for similar components
- Implement proper loading and error states
- Use semantic HTML and ARIA attributes for accessibility

**Template:**

```typescript
export enum ComponentVariant {
  Primary = 'primary',
  Secondary = 'secondary',
}

export enum ComponentSize {
  Small = 'small',
  Medium = 'medium',
  Large = 'large',
}
interface ComponentProps {
  variant?: ComponentVariant;
  size?: ComponentSize;
  className?: string;
  // ... other props
}

const Component: React.FC<HTMLDivElement, ComponentProps> = (props) => {
  const { variant = 'primary', className, ...rest } = props;
  const mergedClassName = twMerge(
    clsx(
      // Base styles using design tokens
      'bg-background-primary text-foreground-primary',
      // Variants
      {
        'bg-accent-primary': variant === 'primary',
        'bg-background-secondary': variant === 'secondary',
      },
      className
    )
  );

  return <div ref={ref} className={mergedClassName} {...props} />;
};
```

**Performance Rules:**

- Lazy load non-critical components:
  `const Component = lazy(() => import('./Component'))`
- Use `React.memo` for expensive renders
- Optimize images with `image/ImageWithFallback`
- Implement proper `key` props for lists
- This is a music application - audio playback performance is critical

### Import Conventions

- Use `@/` prefix for src directory imports
- Organize imports: external libraries, internal modules, relative imports
- Use default exports for page components
- Use named exports for utility functions and hooks
- Try to avoid usage of Next.js components to prevent vendor lock-in and
  unintended cost on Vercel. Can make exceptions for special cases.

### Local Development Notes

- Frontend connects to local backend by default
- DSP artifacts prepared during build process
- Concurrent processes: Next.js dev server + Tailwind watcher
- Bundle analyzer available for optimization analysis
