Import, organize, and manage media assets with folder structure, proxy generation, and dual view modes.
- Importing Media
- View Modes
- Source Thumbnail Cache
- Folder Organization
- Compositions
- Proxy Generation
- Selection
- Context Menu
- Media Properties
- Drag to Timeline
- Project Integration
- Media Relinking
| Type | Formats |
|---|---|
| Video | MP4, WebM, MOV, AVI, MKV, WMV, M4V, FLV |
| Audio | WAV, MP3, OGG, FLAC, AAC, M4A, WMA, AIFF, OPUS |
| Image | PNG, JPG/JPEG, GIF, WebP, BMP, SVG |
| Vector Animation | .lottie, Lottie JSON (.json, content-sniffed) |
The panel also accepts a few specialized asset types that flow into the timeline as 3D clips:
modelfiles: OBJ, glTF/GLB, FBXgaussian-splatfiles: PLY, SPLAT
Numbered .glb files with a shared prefix are detected as a sequence during batch import. A folder containing frame000000.glb, frame000001.glb, frame000002.glb and so on will appear as one model asset instead of many separate files.
Numbered .ply or .splat files with a shared prefix are also detected as a sequence during batch import. A folder containing scan000000.ply, scan000001.ply, scan000002.ply and so on will appear as one gaussian-splat asset instead of many separate files.
Lottie imports are treated as first-class media items. .json files are only accepted when their contents actually match Lottie structure, so arbitrary JSON data is not misclassified as animation.
Click the Import button in the panel header. Uses the File System Access API when available (Chrome/Edge) for native file picker with persistent handles, or falls back to a standard file input.
Click the + Add button for creating new items:
- Composition - New composition (uses active comp's output resolution)
- Folder - New folder for organization
- Text - New text item (placed in auto-created "Text" folder)
- 3D Text - New 3D text mesh item
- Solid - New solid color item (placed in auto-created "Solids" folder)
- Camera - New camera item
- 3D Effector - New shared-scene 3D effector item
- Mesh ▶ - Submenu with 3D primitive meshes (placed in auto-created "Meshes" folder):
- Cube, Sphere, Plane, Cylinder, Torus, Cone
- Creates a
MeshItemwhich can be dragged to the timeline as a 3D clip
- Adjustment Layer - Coming soon
- Gaussian Splat - Import a gaussian-splat asset directly from the add menu
- Drag files directly from the OS file explorer into the Media Panel
- Drag folders directly into the panel; nested folders are recreated inside the project
- Multiple files supported
- Attempts to acquire file handles via
getAsFileSystemHandlefor persistence - Falls back to legacy directory-entry walking where needed
- Falls back to standard File objects when handles are unavailable
Imports use a two-phase approach:
- Phase 1 (instant): A placeholder entry appears immediately in the panel with
isImporting: true, showing file name and size - Phase 2 (background): Full processing runs in the background:
- Media info extraction (dimensions, duration, FPS, codec, bitrate, audio detection)
- Thumbnail generation (for video and image files)
- File hash calculation (for deduplication and proxy matching)
- Copy to project RAW folder when
copyMediaToProjectis enabled, or when the import is forced - Numbered GLB sequence grouping into a single 30fps model-sequence asset
- Numbered PLY/SPLAT sequence grouping into a single 30fps gaussian-splat sequence asset
- Existing proxy detection (by file hash)
Deduplication: Files with matching name + size are automatically skipped.
Batch processing: When importing multiple files, up to 3 files are processed in parallel.
If a project-local Raw/ copy is created, that copy becomes the canonical source for the imported asset. The store promotes the copied handle so later reloads and exports do not depend on the original file.
When supported (Chrome/Edge):
- Native file picker via
showOpenFilePicker - Persistent file handles stored in IndexedDB
- Path information preserved
- Handles from drag-and-drop also captured when available
- If the same media is also copied into the project
Raw/folder, that project copy is preferred on reload
| Size | Behavior |
|---|---|
| < 500MB | Full thumbnails generated |
| > 500MB | Thumbnail generation skipped |
- Created via Add dropdown or context menu
- Uses active composition dimensions (fallback: 1920x1080)
- Default duration: 5 seconds
- Color picker for customization via Solid Settings dialog
- Placed in auto-created "Solids" folder
- Drag to timeline to create solid color clips
- Created via Add dropdown
- Default text: "New Text", font: Arial 48px white
- Default duration: 5 seconds
- Placed in auto-created "Text" folder
- Drag to timeline to create text clips
- Note: These are defaults for Media Panel text items (Arial, 48px). Timeline text clips use different defaults: Roboto, 72px (from
DEFAULT_TEXT_PROPERTIESinstores/timeline/constants.ts). - See Text Clips for full details
The panel supports two view modes through a single header toggle button that swaps between list and grid icons. The selected mode is persisted in localStorage.
- Table layout with sortable, reorderable columns
- Nested folder tree with expand/collapse arrows
- Column headers for sorting and drag-to-reorder
- Resizable name column (120px - 500px range, saved to localStorage)
- Thumbnail grid with file names below each item
- Folder navigation via breadcrumb bar
- Double-click folders to navigate into them
- Breadcrumb shows full path from root, each segment is clickable
- Hover tooltip shows detailed metadata (resolution, duration, codec, bitrate, file size)
- Duration badge overlay on video and composition thumbnails
- Item count badge on folder thumbnails
Video thumbnails are generated per source media file, not per clip instance.
- Generation runs at roughly 1 thumbnail per second of source media
- Split and trimmed clips reuse the same source thumbnail set instead of regenerating thumbnails
- Thumbnails are cached in IndexedDB and promoted into an in-memory URL cache on load
- The cache can also be reused by file hash when the same source is imported again
- Large files above the thumbnail threshold skip this generation path entirely
- Add dropdown -> Folder
- Or right-click -> New Folder
- Folders are created expanded by default
- Nested folders supported
- Drag-and-drop items into folders (single or multi-select)
- Expand/collapse tree view (list mode) or navigate into (grid mode)
- Cycle detection prevents dropping a folder into itself or its descendants
- Label colors assignable to folders
createFolder(name, parentId?) // Create folder (returns MediaFolder)
removeFolder(id) // Delete (moves children to parent)
renameFolder(id, name) // Rename
toggleFolderExpanded(id) // Toggle expand/collapse
moveToFolder(itemIds[], folderId) // Move items (null = root)- Add dropdown -> Composition
- Created with settings from
settingsStore.outputResolution - Default duration: 60 seconds, frame rate: 30 fps
- Starts with one Video track and one Audio track
Edit via right-click -> Composition Settings:
- Width and height
- Frame rate
- Duration
- Resizing adjusts clip transforms to maintain pixel positions
createComposition(name, settings?) // Create with optional overrides
duplicateComposition(id) // Creates "Name Copy"
removeComposition(id) // Delete
updateComposition(id, updates) // Update settings
openCompositionTab(id, options?) // Edit in timeline (with animation)
closeCompositionTab(id) // Close tab
reorderCompositionTabs(from, to) // Drag to reorder tabs
setActiveComposition(id) // Switch active composition
getActiveComposition() // Get current composition
getOpenCompositions() // List open tabs- Compositions open as tabs in the timeline
- Tab switching saves current timeline state and loads the new composition's state
- Animated transitions (exit/enter) when switching between compositions
- Synced playhead when navigating into/out of nested compositions
- Drag composition to timeline to create a nested comp clip
- Double-click composition clip to navigate into it
- Playhead position syncs between parent and nested compositions
- Changes in nested comp reflect in parent timeline
- Double-click a video or image file to open it in the source monitor
- Sets
sourceMonitorFileIdin the store
Proxies require an open project (via projectFileService). For large video files:
- Right-click video -> Generate Proxy
- Proxy frames are generated and stored in the project folder
- Video is decoded frame-by-frame using
proxyGenerator - Frames are saved individually to the project's proxy storage via
projectFileService - Audio is extracted separately in the background (non-blocking)
- Generation can be cancelled; partial proxies are preserved
- Resumed automatically if a partial proxy exists on disk
FPS: 30 // Constant frame rate for proxyA proxy is considered complete when >= 98% of expected frames are available:
frameCount >= Math.ceil(duration * PROXY_FPS) * 0.98interface MediaFile {
proxyStatus: 'none' | 'generating' | 'ready' | 'error';
proxyProgress: number; // 0-100
proxyFrameCount?: number; // Total frames generated
proxyFps?: number; // Always 30
hasProxyAudio?: boolean; // Audio proxy extracted
}Toggle proxy playback mode via proxyEnabled / toggleProxyEnabled():
- When enabled, mutes all video elements in the timeline
- Uses proxy frames instead of original video for playback
| Badge | Meaning |
|---|---|
| P (blue) | Proxy ready |
| P (filling animation) + X% | Generating, with progress |
- Click - Select single item
- Ctrl/Cmd + Click - Toggle item in selection
- Shift + Click - Add to selection
- Click and drag on empty space in the item list to draw a selection rectangle
- 4px movement threshold before marquee activates
- Hold Ctrl/Cmd while marquee selecting to add to existing selection
- Works in both list and grid view modes
16 AE-style label colors assignable to any item (files, folders, compositions, text, solids):
none, red, yellow, blue, green, purple, orange, pink, cyan, brown, lavender, peach, seafoam, fuchsia, tan, aqua
Click the label dot in the list view to open the color picker. When multiple items are selected, the color is applied to all selected items.
Right-click on items or empty space for context options.
- Import Media...
- New Composition
- New Folder
- New Text
- New Solid
- Mesh ▶ submenu: Cube, Sphere, Plane, Cylinder, Torus, Cone
- Rename (single selection only)
- Move to Folder submenu (shows available folders + "Root")
- Delete (shows count for multi-selection)
- Generate Proxy / Stop Proxy Generation (X%) / Proxy Ready (disabled)
- Show in Explorer submenu:
- Raw (downloads file if no native path)
- Proxy (disabled if no proxy)
- Set Proxy Folder...
- Composition Settings... (opens settings dialog)
- Solid Settings... (opens color/dimension editor)
The media list displays items in a table with the following columns:
| Column | Description | Example |
|---|---|---|
| Name | File name with AE-style file type icon | Video.mp4 |
| Label | Colored dot indicator (clickable) | colored circle |
| Duration | Clip length (m:ss) | 4:02 |
| Resolution | Width x Height | 1920x1080 |
| FPS | Frame rate (video) or composition frame rate | 25 |
| Container | File container format | MP4, MKV, WebM |
| Codec | Video codec | H.264, VP9, AV1 |
| Audio | Has audio track? | Yes / No |
| Bitrate | Data rate | 12.5 Mbps |
| Size | File size | 125.4 MB |
Sortable Columns:
- Click column header to sort ascending
- Click again for descending
- Click a third time to remove sort
- Folders always sort separately (stay at top)
Reorderable Columns:
- Drag column headers to rearrange order
- Order is saved in localStorage (
media-panel-column-order)
Resize Name Column:
- Drag the vertical resize handle on the right edge of the Name column
- Width range: 120px - 500px
- Width saved in localStorage (
media-panel-name-width)
| Badge | Meaning |
|---|---|
| P (blue) | Proxy ready |
| P (filling) + % | Proxy generating |
| T (green) | Fully transcribed - click to open transcript |
| T (filling) | Partially transcribed - shows coverage % |
| A (orange) | Fully analyzed - click to open analysis |
| A (filling) | Partially analyzed - shows coverage % |
Clicking transcript or analysis badges selects the corresponding clip in the timeline and opens the clip properties panel.
interface MediaFile {
id: string;
name: string;
type: 'video' | 'audio' | 'image' | 'lottie' | 'rive';
file?: File; // Undefined when needs reload
url: string;
parentId: string | null;
createdAt: number;
duration?: number;
width?: number;
height?: number;
fps?: number; // Frame rate (video)
codec?: string; // H.264, VP9, AV1, ProRes, etc.
audioCodec?: string; // AAC, AC-3, Opus, etc.
container?: string; // MP4, MKV, WebM, etc.
fileSize?: number; // File size in bytes
bitrate?: number; // Bits per second
hasAudio?: boolean; // Whether video has audio tracks
thumbnailUrl?: string;
fileHash?: string; // For dedup and proxy matching
labelColor?: LabelColor; // 16-color label system
isImporting?: boolean; // True during background import
// Proxy
proxyStatus?: ProxyStatus;
proxyProgress?: number;
proxyVideoUrl?: string; // URL to proxy video
proxyFrameCount?: number;
proxyFps?: number;
hasProxyAudio?: boolean;
// Transcript
transcriptStatus?: TranscriptStatus;
transcript?: TranscriptWord[];
transcriptCoverage?: number;
transcribedRanges?: [number, number][]; // Time ranges that have been transcribed
// Analysis
analysisStatus?: AnalysisStatus;
analysisCoverage?: number;
vectorAnimation?: VectorAnimationMetadata;
// File System Access API
hasFileHandle?: boolean;
filePath?: string;
absolutePath?: string;
projectPath?: string; // Path within project RAW folder
}- Select media in panel
- Drag to timeline
- Drop on appropriate track
| Item Type | Drag Payload Kind | Data Transfer Key |
|---|---|---|
| Media file (video/image/lottie) | media-file |
application/x-media-file-id |
| Media file (audio) | media-file (marked as audio) |
application/x-media-file-id |
| Composition | composition |
application/x-composition-id |
| Text item | text |
application/x-text-item-id |
| Solid item | solid |
application/x-solid-item-id |
| Mesh item | mesh |
application/x-mesh-item-id |
| Folder | Internal move only (no timeline drop) | — |
- Creates clip from media source
- Uses actual media duration
- Audio-only files restricted to audio tracks
- Files still importing or missing cannot be dragged to timeline
- Numbered gaussian-splat sequences always stay on the shared Three.js 3D renderer path after drop
- Compositions cannot be dragged into themselves (active comp check)
- Mesh items create 3D clips with
is3D: trueandmeshType(rendered via Three.js)
| Media Type | Allowed Tracks |
|---|---|
| Video/Image/Lottie/Composition/Text/Solid/Mesh | Video tracks only |
| Audio | Audio tracks only |
Media references are saved with the project file, while IndexedDB keeps the handle cache and other reload helpers:
- File metadata (name, type, dimensions, duration, codec, etc.)
- File handles (for reload on next session)
- Folder structure
- Composition state with timeline data
- Text items and solid items (via localStorage)
- When present,
projectPathpoints at the copiedRaw/<name>file and is used for automatic relinking
On project load:
- Project-local
Raw/copies are tried first and become the canonical source when available - Media metadata restored from IndexedDB and project JSON
- File handles used to restore file access when no
Raw/copy is available - Thumbnails restored from
Cache/thumbnailsby file hash - Existing proxies detected automatically, including legacy media-id based storage
- Existing transcripts and analysis data loaded from the project folder
- Dead blob/object URLs are regenerated for available files
- If a retained
Fileobject is still present, image/video thumbnails are rebuilt when needed after refresh - Folder structure, expansion state, dock layout, and per-composition view state restored
- Each media has a unique timestamp-based ID
- Clips reference media by
mediaFileId - Survives project reload
- File hash used for proxy and thumbnail deduplication across reimports
When media files lose access (e.g., after browser restart):
- Automatic detection - Panel shows "Relink (N)" button when files need reload
- Relink dialog - Click the button to open the relink interface
- Files dimmed in list with
no-filestyling when unavailable
Files are reloaded in priority order:
- Project RAW folder - If the asset was copied into the project and the project is open
- Stored file handle - Re-access the original file location, including permission re-checks
On project load, the app also tries to auto-relink missing files silently from Raw/ and then falls back to stored handles in IndexedDB. This is case-insensitive on filename only; there is no content-hash relink pass.
Double-clicking a file that has lost access triggers a single-file reload attempt with permission request.
| State | Appearance |
|---|---|
| File missing/needs reload | Row dimmed, no-file class |
| File importing | importing class with loading state |
| Proxy available | Blue "P" badge |
The media store is split into modular slices:
| Slice | File | Responsibility |
|---|---|---|
| fileImportSlice | slices/fileImportSlice.ts |
Import via picker, drag-drop, handles |
| fileManageSlice | slices/fileManageSlice.ts |
Remove, rename, reload files |
| compositionSlice | slices/compositionSlice.ts |
CRUD, tabs, active composition switching |
| slotSlice | slices/slotSlice.ts |
Resolume-style slot grid assignments |
| multiLayerSlice | slices/multiLayerSlice.ts |
Multi-layer playback activation |
| folderSlice | slices/folderSlice.ts |
Folder CRUD and expand/collapse |
| selectionSlice | slices/selectionSlice.ts |
Selection, move-to-folder, label colors |
| proxySlice | slices/proxySlice.ts |
Proxy generation, cancellation, progress |
| projectSlice | slices/projectSlice.ts |
Save, load, init from DB |
Inline actions (in index.ts): createTextItem, removeTextItem, getOrCreateTextFolder, createSolidItem, removeSolidItem, updateSolidItem, getOrCreateSolidFolder, createMeshItem, removeMeshItem, getOrCreateMeshFolder, getItemsByFolder, getItemById, getFileByName.
Boot Sequence: init.ts handles IndexedDB initialization, timeline restore from saved state, status synchronization, auto-save interval setup, beforeunload handler, and audio cleanup via disposeAllAudio().
Helper modules in helpers/:
| Module | Purpose |
|---|---|
importPipeline.ts |
Unified import processing -- orchestrates the two-phase import (placeholder then background processing) |
mediaInfoHelpers.ts |
Codec detection, metadata extraction (uses mp4box for MP4 container parsing) |
thumbnailHelpers.ts |
Thumbnail generation, deduplication by file hash, skip logic for large files |
fileHashHelpers.ts |
File hash calculation for deduplication and proxy matching |
| Test File | Tests | Coverage |
|---|---|---|
fileManageSlice.test.ts |
106 | Files, folders, solids, text items, selection, labels |
compositionSlice.test.ts |
101 | Compositions |
Run tests: npx vitest run
- Cloud storage integration
- Asset library across projects
- Batch import settings
- Adjustment layers (UI placeholder exists)
- Timeline - Using media in edits
- Audio - Audio media handling
- Project Persistence - Saving
- Export - Rendering output
Source: src/components/panels/MediaPanel.tsx, src/stores/mediaStore/index.ts, src/stores/mediaStore/slices/