How to Add New Track Types to Clypra's Timeline Store
Adding a new track type to Clypra's timeline store requires extending the TrackType union in the types definition and registering a corresponding UI height in the Zustand store's trackHeights lookup table.
Clypra stores every timeline element—including tracks, clips, gaps, transitions, and markers—in a single Zustand store located at src/store/timelineStore.ts. To successfully add new track types such as video, audio, text, sticker, or filter variants to Clypra's timeline store, you must declare the type string in the TypeScript union and configure its visual representation, while the generic store mutations handle the rest automatically.
Understanding the Timeline Architecture
The timeline store operates on a generic Track array without inspecting specific type values for core operations. The TrackType union, defined in src/types/index.ts, controls which strings are valid when creating tracks. When a track is instantiated via addTrack or insertTrackAt, the store references a trackHeights object (lines 17‑23 in src/store/timelineStore.ts) to assign the default pixel height. This architecture means you can introduce entirely new track categories—such as subtitles or custom effects—without modifying gap detection, ripple editing, or epoch handling logic.
Step-by-Step Guide to Adding New Track Types
Step 1: Extend the TrackType Union
First, declare the new track type in the central types file. Open src/types/index.ts and add your new type string to the TrackType union:
// src/types/index.ts
export type TrackType =
| "video"
| "audio"
| "text"
| "sticker"
| "filter"
| "video-effect"
| "body-effect"
| "animated-overlay"
// Add your new type here
| "subtitle";
This TypeScript definition ensures type safety throughout the application when the new track type is referenced.
Step 2: Register UI Height in trackHeights
Next, configure the visual height for the new track type in the store. In src/store/timelineStore.ts, locate the trackHeights object (lines 17‑23) and add a mapping for your new type:
// src/store/timelineStore.ts
const trackHeights: Record<string, number> = {
video: 68,
audio: 52,
text: 30,
sticker: 30,
filter: 30,
"video-effect": 30,
"body-effect": 30,
"animated-overlay": 30,
// Height for the new track type
subtitle: 30,
};
The store uses this value to set the height property when creating tracks, ensuring consistent UI rendering.
Step 3: (Optional) Customize Insertion Logic
By default, new tracks append to the timeline based on generic rules. If your track type requires specific placement—such as always appearing below the main video track—modify the insertion helpers. Update getInsertIndexForNewTrack (lines 29‑38) or getInsertIndexForNewTrackGrouped (lines 41‑71) in src/store/timelineStore.ts:
// src/store/timelineStore.ts
export function getInsertIndexForNewTrack(
tracks: Track[],
trackType: TrackType
): number {
if (trackType === "subtitle") {
// Place subtitles immediately after the main video track
const mainIdx = tracks.findIndex((t) => t.type === "video");
return mainIdx >= 0 ? mainIdx + 1 : tracks.length;
}
// Existing logic for other types...
return tracks.length;
}
This optional step ensures logical grouping when users add multiple track types.
Step 4: Create Tracks Using the New Type
With the configuration complete, create tracks using the standard store methods. The addTrack method accepts any valid TrackType string:
import { useTimelineStore } from "@/store/timelineStore";
function onAddSubtitleTrack() {
useTimelineStore.getState().addTrack("subtitle");
}
The store automatically applies the configured height and executes all standard lifecycle operations.
How Generic Store Operations Handle New Types
The timeline store's mutation methods—including addTrack, insertTrackAt, and removeTrack—are designed to accept any TrackType value. Because gap detection, ripple editing, and clip management operate on the generic Track[] array without inspecting the specific type field, these features work immediately with your new track type. UI components in src/components/editor/timeline/index.ts render tracks by reading the type and height properties directly from the store, so new types appear automatically without component modifications.
Summary
- Extend
TrackTypeinsrc/types/index.tsto declare valid type strings - Configure
trackHeightsinsrc/store/timelineStore.ts(lines 17‑23) to set pixel heights - Optionally customize
getInsertIndexForNewTrack(lines 29‑38) orgetInsertIndexForNewTrackGrouped(lines 41‑71) for specific insertion rules - Use
addTrack()with the new type string to create instances - Leverage automatic handling for gaps, ripple editing, and UI rendering without additional code changes
Frequently Asked Questions
Do I need to modify the track removal logic when adding a new type?
No. The removeTrack method in src/store/timelineStore.ts operates on track IDs rather than types, so it handles new track types automatically without modification.
Will ripple editing and gap detection work with custom track types?
Yes. These features rely on the generic Track array structure and timestamp calculations rather than specific type values, so they function immediately for any track added to the store.
How do I set different heights for sub-types of tracks?
Add distinct entries to the trackHeights object in src/store/timelineStore.ts for each variation. For example, "subtitle": 25 and "caption": 35 can coexist with separate height values.
Where should I place custom insertion logic if I want tracks grouped by type?
Implement the grouping logic in the getInsertIndexForNewTrackGrouped function (lines 41‑71) in src/store/timelineStore.ts, which is designed specifically for type-aware positioning algorithms.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →