

## Overview [#overview]

A complete file management system for browsing, selecting, and uploading files.

Use `Files` for the full compound UI, or `Files.Provider` plus lower-level pieces when you need a custom layout.

## Key Concepts [#key-concepts]

### Raw Rows In, UI Out [#raw-rows-in-ui-out]

Pass your raw `FileAsset` rows. Files maps them to UI labels, icons, badges, and status display.

### Upload Progress Overlay [#upload-progress-overlay]

Pass `UploadProgressState` (or `UploadProgressLike`) from your upload state. Active progress entries (0-100) override row status until complete.

### Composable Components [#composable-components]

* `Files`: full compound file manager
* `Files.Provider`: headless state provider for custom layouts
* `FileDetails`: composable details panel
* `FileListView`, `FileGridView`: browse primitives

## Data Contract [#data-contract]

### FileAsset [#fileasset]

`FileAsset` is a discriminator union keyed by `asset_type`.

```ts
type FileAsset = StandardFileAsset | VirtualFileAsset;

type FileAssetBase = {
  id: string;
  name: string;
  status: "active" | "archived" | "waiting" | "pending" | "failed";
  created_at: Date | string | null;
  updated_at: Date | string | null;
  created_by: string | null;
  created_by_image_url?: string | null;
  indicators?: FileIndicator[];
  icon?: React.ReactNode;
};

type StandardFileAsset = FileAssetBase & {
  asset_type: "standard";
  mime_type: string;
  size: number | bigint | string | null;
};

type VirtualFileAsset = FileAssetBase & {
  asset_type: "virtual";
  virtual_type: string;
};
```

What this means for consumers:

* `asset_type: "standard"` requires `mime_type` and `size`.
* `asset_type: "virtual"` requires `virtual_type`.
* Do not mix standard and virtual-only fields on the same row.

### FileIndicator [#fileindicator]

```ts
type FileIndicator = {
  label: string;
  icon: React.ReactNode;
};
```

Indicators render as compact badges on list/grid items (up to 2).

### Upload Progress [#upload-progress]

```ts
type UploadProgressState = {
  percentage: number; // 0-100
  filesCompleted: number;
  totalFiles: number;
  fileProgress: Map<
    string,
    {
      id: string;
      percentage: number; // 0-100
      status: "waiting" | "pending" | "uploading" | "completed" | "failed";
      etaSeconds?: number;
    }
  >;
};
```

### Virtual Files [#virtual-files]

Use virtual rows for derived content (for example transcripts or summaries) that does not map to a real uploaded blob.

```tsx
import { Scroll } from "@tilt-legal/cubitt-icons/ui/outline";

const transcript: FileAsset = {
  id: "virtual-1",
  name: "Meeting Notes Transcript",
  asset_type: "virtual",
  virtual_type: "transcript",
  status: "active",
  created_by: "System",
  created_at: "2025-01-10T09:00:00.000Z",
  updated_at: "2025-01-10T09:00:00.000Z",
  icon: <Scroll />,
};
```

Virtual files often shouldn't be renameable. Use the `canRename` predicate on `Files` to disable rename for specific files:

```tsx
<Files
  files={files}
  onRename={handleRename}
  canRename={(file) => !file.isVirtual}
>
```
