# 004: Atoms System

## Overview

Implement the atoms system for creative assets (songs, videos, images, lyrics, interactive webviews). Atoms are shareable creative units that can be created, attached to messages, filtered, and displayed throughout Spaces. This phase assumes 002 (core features) is complete.

## Goals

- Implement complete schema for atoms with async creation support
- Build atom CRUD operations with permissions
- Implement async atom creation with status/progress tracking
- Create atom attachment system for messages
- Build atom filtering and querying
- Implement UI for atom creation, display, and management
- Support multiple atom types with type-specific metadata

## Schema Implementation

### Atoms Table
- `id`: Unique identifier
- `type`: Enum (song, video, image, lyrics, webview)
- `ownerId`: User who created the atom
- `spaceId`: Space where atom was created
- `metadata`: Type-specific metadata (JSON structure varies by type)
- `status`: Enum (pending, processing, completed, failed)
- `progress`: Number (0-100) for creation progress
- `createdAt`: Timestamp
- `updatedAt`: Timestamp

### Message-Atom Relationships
- Messages can reference multiple atoms
- Atoms can be attached to messages via `atomReferences` field in Messages table

## Backend Functions (Convex)

### Atom Operations
- **Create atom**: Initiate async atom creation with status/progress tracking
  - Input: type, metadata, spaceId
  - Output: atomId with initial status "pending"
  - Permissions: Space members only
- **Update atom metadata**: Modify atom details
  - Permissions: Owner only
- **Update atom status/progress**: Internal function for tracking creation
  - Called by async jobs/Modal functions
- **Delete atom**: Remove atom and clean up references
  - Permissions: Owner only
- **Query atoms**: List atoms with filtering
  - Filter by: type, owner, space, date range, status
  - Support pagination
  - Permissions: Space members can query space atoms
- **Get atom by ID**: Retrieve single atom with full metadata
  - Permissions: Space members can access space atoms
- **Attach atom to message**: Add atom reference when sending message
  - Validated during message creation
  - Permissions: Space members only

### Async Creation Flow
1. User initiates atom creation (e.g., "create song from prompt")
2. Backend creates atom record with status="pending", progress=0
3. Backend triggers async job (Modal/external API)
4. Job periodically updates atom progress (0-100)
5. On completion: status="completed", progress=100, metadata updated
6. On failure: status="failed", error message stored

## Permission System

- **Space members** can: create atoms in their space, view all space atoms
- **Atom owners** can: update metadata, delete their own atoms
- **System/jobs** can: update status and progress for any atom

## UI Components

### Atom Creation Interface
- Type selector (song, video, image, lyrics, webview)
- Type-specific input forms
- Creation trigger button
- Real-time progress indicator during async creation
- Status badges (pending, processing, completed, failed)
- Error display for failed creations

### Atom Display Widgets
Type-specific renderers:
- **Song**: Audio player, title, artist, album art, duration
  - **Display Context**: Songs appear as rows beneath their generating message
  - **Component Structure**: `<SongAtomRow />` - componentized for easy iteration
  - **Row Contents**:
    - Album art thumbnail (or placeholder during generation)
    - Title and artist
    - Duration (when available)
    - Status indicator (pending/streaming/complete/error)
    - Play/pause button (disabled until playable)
  - **Future Variations**: Mobile web compact view, canvas view, playlist view
  - **Status-Based Rendering**:
    - `pending`/`queued`: Show spinner, "Generating..."
    - `streaming`: Show progress bar, "Finalizing..."
    - `complete`: Full metadata, playable
    - `error`/`failed`: Error message, retry option
- **Video**: Video player, thumbnail, title, duration
- **Image**: Image viewer, title, dimensions
- **Lyrics**: Formatted text display, song association
- **Webview**: Embedded iframe/interactive widget preview

### Atom Cards
Two display contexts:
1. **Inline (in messages)**: Compact card with preview
2. **Standalone (in filters/galleries)**: Full card with details

### Atom Filtering Interface
- Filter panel with:
  - Type filter (multi-select)
  - Owner filter (user selector)
  - Date range picker
  - Status filter (for debugging)
- Grid or list view toggle
- Sort options (newest, oldest, by type, by owner)

### Atom Attachment UI (in Message Composer)
- "Attach atom" button
- Atom picker modal/dropdown
- Search/filter atoms
- Preview selected atoms before sending
- Inline atom display in message preview

### Atom Management (Owner View)
- My atoms list
- Edit metadata button
- Delete atom button (with confirmation)
- Status/progress indicators
- Re-trigger failed creations (future)

## Acceptance Criteria

- [ ] Users can create atoms of all types (song, video, image, lyrics, webview)
- [ ] Atom creation shows real-time progress for async operations
- [ ] Failed atom creations display errors clearly
- [ ] Users can attach atoms to messages
- [ ] Attached atoms render correctly inline in message feed
- [ ] Atom filtering works by type, owner, date, and status
- [ ] Atom cards display correctly in both inline and standalone contexts
- [ ] Users can edit their own atom metadata
- [ ] Users can delete their own atoms
- [ ] Deleting an atom removes it from message references gracefully
- [ ] Atoms from external spaces are not visible/attachable in current space
- [ ] All atom permissions are enforced server-side

## Technical Considerations

### Async Creation Architecture

**Atom creation is generally an async process with optional progress streaming.** Implement:
- Status tracking (pending, processing, completed, failed)
- Progress percentage (0-100)
- Visual feedback in UI during creation
- Error handling for failed atoms
- Convex actions or scheduled functions to poll/update progress
- Integration with Modal or external APIs for actual generation

**Song Generation Implementation:**

Songs use the Suno API client (`lib/sunoClient.ts`). The Suno API **returns two songs per generation request**. Both songs should be attached to the requesting message/atom.

**Important Suno API Details:**
- Songs have statuses: `submitted`, `queued`, `streaming`, `complete`, `error`, `failed`
- A song is ready to play when status transitions beyond `streaming`
- Song duration is only known when status is `complete`
- The API should be polled periodically to update non-terminal song statuses

Example Convex implementation flow:

```typescript
// convex/atoms.ts
import { action, mutation } from './_generated/server';
import { v } from 'convex/values';
import { createSunoClient } from '../lib/sunoClient';

export const generateSong = action({
  args: {
    prompt: v.string(),
    tags: v.optional(v.string()),
    makeInstrumental: v.optional(v.boolean()),
    spaceId: v.id('spaces'),
    userId: v.id('users'),
  },
  handler: async (ctx, args) => {
    const client = createSunoClient();

    // 1. Generate songs (creates 2 songs)
    const songIds = await client.generateSongs({
      prompt: args.prompt,
      makeInstrumental: args.makeInstrumental,
    });

    // 2. Create atom records for BOTH songs with status='pending'
    const atomIds = await Promise.all(
      songIds.map(songId =>
        ctx.runMutation(api.atoms.create, {
          type: 'song',
          spaceId: args.spaceId,
          ownerId: args.userId,
          metadata: {
            sunoClipId: songId, // Store Suno clip ID for remixing later
            title: 'Generating...',
          },
          status: 'pending',
          progress: 0,
        })
      )
    );

    // 3. Schedule periodic polling for these atoms
    await ctx.scheduler.runAfter(0, api.atoms.pollSongStatus, {
      atomIds,
    });

    return atomIds;
  },
});

// Scheduled function to poll Suno API for status updates
export const pollSongStatus = mutation({
  args: {
    atomIds: v.array(v.id('atoms')),
  },
  handler: async (ctx, args) => {
    // Get atoms from DB
    const atoms = await Promise.all(
      args.atomIds.map(id => ctx.db.get(id))
    );

    // Filter only non-terminal atoms
    const activeAtoms = atoms.filter(
      atom => atom && !['completed', 'failed'].includes(atom.status)
    );

    if (activeAtoms.length === 0) return;

    // Poll Suno API
    const client = createSunoClient();
    const sunoIds = activeAtoms.map(a => a.metadata.sunoClipId);
    const songs = await client.getSongStatus(sunoIds);

    // Update atom records
    for (let i = 0; i < activeAtoms.length; i++) {
      const atom = activeAtoms[i];
      const song = songs[i];

      await ctx.db.patch(atom._id, {
        metadata: {
          ...atom.metadata,
          title: song.title || atom.metadata.title,
          artist: song.artist,
          audioUrl: song.audioUrl,
          videoUrl: song.videoUrl,
          albumArtUrl: song.albumArtUrl,
          duration: song.duration,
          sunoStatus: song.status, // Store raw Suno status
        },
        status: song.status,
        progress: song.status === 'completed' ? 100 :
                 song.status === 'streaming' ? 75 :
                 song.status === 'queued' ? 25 : 50,
        updatedAt: Date.now(),
      });
    }

    // Schedule next poll if any atoms still active
    const stillActive = songs.some(
      s => !['complete', 'error', 'failed'].includes(s.status)
    );

    if (stillActive) {
      await ctx.scheduler.runAfter(2000, api.atoms.pollSongStatus, {
        atomIds: args.atomIds,
      });
    }
  },
});
```

### Type-Specific Metadata

Each atom type has its own metadata structure but shares common patterns:
- **Song**: `{ sunoClipId, title, artist, audioUrl, videoUrl?, albumArtUrl, duration, sunoStatus, tags?, prompt? }`
  - **Implementation**: Uses Suno API via `lib/sunoClient.ts`
  - **Generation**: `/api/generate/v2-web` endpoint (generates 2 songs per request)
    - Request payload (minimal): `{ prompt, tags?, make_instrumental?, mv: 'chirp-crow' }`
    - Returns 2 song IDs immediately
  - **Status Polling**: `/api/feed/v3?ids=id1,id2` endpoint
  - **Environment Variables** (set in Convex dashboard):
    - `SUNO_BASE_URL`: Base URL for Suno API
    - `SUNO_SESSION_TOKEN`: Long-lived session token for authentication
  - **Song Statuses** (Suno API):
    - `submitted` → `queued` → `streaming` → `complete`
    - Or: `submitted` → `error`/`failed`
    - Song is playable when status > `streaming`
    - Duration only available when `complete`
  - **Flow**:
    1. User provides prompt (and optional tags, instrumental flag)
    2. Call `generateSongs()` to get 2 song IDs immediately
    3. Create 2 atom records with status='pending', store `sunoClipId`
    4. Schedule periodic polling via Convex scheduled function
    5. Update atoms with metadata as status progresses
    6. Stop polling when both songs reach terminal state (`complete`/`error`/`failed`)
- **Video**: `{ title, videoUrl, thumbnailUrl, duration, resolution }` (not yet implemented)
- **Image**: `{ title, imageUrl, width, height, format }` (not yet implemented)
- **Lyrics**: `{ title, text, associatedSongId? }` (not yet implemented)
- **Webview**: `{ title, url, embedHtml?, thumbnail? }` (not yet implemented)

Store as JSON in `metadata` field, validate structure per type.

### Message-Atom References

Atoms are referenced in messages, not embedded. Store atom IDs in message `atomReferences` array. When rendering messages:
- Fetch atom data by IDs
- Handle deleted atoms gracefully (show placeholder or hide)
- Lazy-load atom previews for performance

### Filtering Performance

For large atom collections:
- Use Convex indexes on type, ownerId, spaceId, createdAt, status
- Implement pagination (cursor-based or offset)
- Consider caching filtered results client-side

### Mobile Considerations

- Atom creation UI must work on mobile (touch-friendly forms)
- Video/audio players must use mobile-compatible controls
- Image viewers should support pinch-to-zoom
- Atom picker modal should be mobile-responsive

## Out of Scope (Future Phases)

- Sampling and remixing atoms
- Advanced progress streaming UI (beyond basic percentage bar)
- Atom versioning/history
- Collaborative atom editing
- Atom collections/playlists
- Cross-space atom sharing
- Atom reactions (separate from message reactions)
- Atom comments/threads
- Audio waveform visualization
- Video trimming/editing in-app
- Batch atom operations
