summaryrefslogtreecommitdiff
path: root/apps/web/src/types
diff options
context:
space:
mode:
authorMaze <167211895+mazeincoding@users.noreply.github.com>2026-01-31 00:20:04 +0100
committerGitHub <noreply@github.com>2026-01-31 00:20:04 +0100
commit7bf0984698e43b9aa2dd879f65951318ed410390 (patch)
treeccf2cd590a47d550152afc07129cb55c71a9ec35 /apps/web/src/types
parent0173db99448d1c4c6415dcf15272e17551b48b04 (diff)
downloadopencut-7bf0984698e43b9aa2dd879f65951318ed410390.tar.gz
codebase overhaul (#697)
Diffstat (limited to 'apps/web/src/types')
-rw-r--r--apps/web/src/types/assets.ts9
-rw-r--r--apps/web/src/types/blog.ts92
-rw-r--r--apps/web/src/types/drag.ts21
-rw-r--r--apps/web/src/types/editor.ts29
-rw-r--r--apps/web/src/types/export.ts33
-rw-r--r--apps/web/src/types/keybinding.ts124
-rw-r--r--apps/web/src/types/language.ts4
-rw-r--r--apps/web/src/types/media.ts17
-rw-r--r--apps/web/src/types/playback.ts21
-rw-r--r--apps/web/src/types/post.ts92
-rw-r--r--apps/web/src/types/project.ts74
-rw-r--r--apps/web/src/types/sounds.ts62
-rw-r--r--apps/web/src/types/stickers.ts3
-rw-r--r--apps/web/src/types/time.ts1
-rw-r--r--apps/web/src/types/timeline.ts286
-rw-r--r--apps/web/src/types/transcription.ts47
16 files changed, 491 insertions, 424 deletions
diff --git a/apps/web/src/types/assets.ts b/apps/web/src/types/assets.ts
new file mode 100644
index 00000000..c8dddfc7
--- /dev/null
+++ b/apps/web/src/types/assets.ts
@@ -0,0 +1,9 @@
+import type { MediaAssetData } from "@/services/storage/types";
+
+export type MediaType = "image" | "video" | "audio";
+
+export interface MediaAsset
+ extends Omit<MediaAssetData, "size" | "lastModified"> {
+ file: File;
+ url?: string;
+}
diff --git a/apps/web/src/types/blog.ts b/apps/web/src/types/blog.ts
new file mode 100644
index 00000000..16795b02
--- /dev/null
+++ b/apps/web/src/types/blog.ts
@@ -0,0 +1,92 @@
+export type Post = {
+ id: string;
+ slug: string;
+ title: string;
+ content: string;
+ description: string;
+ coverImage: string;
+ publishedAt: Date;
+ updatedAt: Date;
+ authors: {
+ id: string;
+ name: string;
+ image: string;
+ }[];
+ category: {
+ id: string;
+ slug: string;
+ name: string;
+ };
+ tags: {
+ id: string;
+ slug: string;
+ name: string;
+ }[];
+ attribution: {
+ author: string;
+ url: string;
+ } | null;
+};
+
+export type Pagination = {
+ limit: number;
+ currpage: number;
+ nextPage: number | null;
+ prevPage: number | null;
+ totalItems: number;
+ totalPages: number;
+};
+
+export type MarblePostList = {
+ posts: Post[];
+ pagination: Pagination;
+};
+
+export type MarblePost = {
+ post: Post;
+};
+
+export type Tag = {
+ id: string;
+ name: string;
+ slug: string;
+};
+
+export type MarbleTag = {
+ tag: Tag;
+};
+
+export type MarbleTagList = {
+ tags: Tag[];
+ pagination: Pagination;
+};
+
+export type Category = {
+ id: string;
+ name: string;
+ slug: string;
+};
+
+export type MarbleCategory = {
+ category: Category;
+};
+
+export type MarbleCategoryList = {
+ categories: Category[];
+ pagination: Pagination;
+};
+
+export type Author = {
+ id: string;
+ name: string;
+ image: string;
+};
+
+export type MarbleAuthor = {
+ author: Author;
+};
+
+export type MarbleAuthorList = {
+ authors: Author[];
+ pagination: Pagination;
+};
diff --git a/apps/web/src/types/drag.ts b/apps/web/src/types/drag.ts
new file mode 100644
index 00000000..e6e5daba
--- /dev/null
+++ b/apps/web/src/types/drag.ts
@@ -0,0 +1,21 @@
+interface BaseDragData {
+ id: string;
+ name: string;
+}
+
+export interface MediaDragData extends BaseDragData {
+ type: "media";
+ mediaType: "image" | "video" | "audio";
+}
+
+export interface TextDragData extends BaseDragData {
+ type: "text";
+ content: string;
+}
+
+export interface StickerDragData extends BaseDragData {
+ type: "sticker";
+ iconName: string;
+}
+
+export type TimelineDragData = MediaDragData | TextDragData | StickerDragData;
diff --git a/apps/web/src/types/editor.ts b/apps/web/src/types/editor.ts
index 6cbc6a6a..0413b208 100644
--- a/apps/web/src/types/editor.ts
+++ b/apps/web/src/types/editor.ts
@@ -1,28 +1 @@
-export type BackgroundType = "blur" | "mirror" | "color";
-
-export type CanvasMode = "preset" | "original" | "custom";
-
-export interface CanvasSize {
- width: number;
- height: number;
-}
-
-export interface CanvasPreset {
- name: string;
- width: number;
- height: number;
-}
-
-export interface TextElementDragState {
- isDragging: boolean;
- elementId: string | null;
- trackId: string | null;
- startX: number;
- startY: number;
- initialElementX: number;
- initialElementY: number;
- currentX: number;
- currentY: number;
- elementWidth: number;
- elementHeight: number;
-}
+export type TPlatformLayout = "tiktok";
diff --git a/apps/web/src/types/export.ts b/apps/web/src/types/export.ts
index 093268ce..351f5fc4 100644
--- a/apps/web/src/types/export.ts
+++ b/apps/web/src/types/export.ts
@@ -1,18 +1,27 @@
-export type ExportFormat = "mp4" | "webm";
-export type ExportQuality = "low" | "medium" | "high" | "very_high";
+export const EXPORT_QUALITY_VALUES = [
+ "low",
+ "medium",
+ "high",
+ "very_high",
+] as const;
+
+export const EXPORT_FORMAT_VALUES = ["mp4", "webm"] as const;
+
+export type ExportFormat = (typeof EXPORT_FORMAT_VALUES)[number];
+export type ExportQuality = (typeof EXPORT_QUALITY_VALUES)[number];
export interface ExportOptions {
- format: ExportFormat;
- quality: ExportQuality;
- fps?: number;
- includeAudio?: boolean;
- onProgress?: (progress: number) => void;
- onCancel?: () => boolean;
+ format: ExportFormat;
+ quality: ExportQuality;
+ fps?: number;
+ includeAudio?: boolean;
+ onProgress?: ({ progress }: { progress: number }) => void;
+ onCancel?: () => boolean;
}
export interface ExportResult {
- success: boolean;
- buffer?: ArrayBuffer;
- error?: string;
- cancelled?: boolean;
+ success: boolean;
+ buffer?: ArrayBuffer;
+ error?: string;
+ cancelled?: boolean;
}
diff --git a/apps/web/src/types/keybinding.ts b/apps/web/src/types/keybinding.ts
index 831f3abd..f9b84e31 100644
--- a/apps/web/src/types/keybinding.ts
+++ b/apps/web/src/types/keybinding.ts
@@ -1,73 +1,71 @@
-import { ActionWithOptionalArgs } from "@/constants/actions";
+import type { TActionWithOptionalArgs } from "@/lib/actions";
/**
* Alt is also regarded as macOS OPTION (⌥) key
* Ctrl is also regarded as macOS COMMAND (⌘) key (NOTE: this differs from HTML Keyboard spec where COMMAND is Meta key!)
*/
export type ModifierKeys =
- | "ctrl"
- | "alt"
- | "shift"
- | "ctrl+shift"
- | "alt+shift"
- | "ctrl+alt"
- | "ctrl+alt+shift";
+ | "ctrl"
+ | "alt"
+ | "shift"
+ | "ctrl+shift"
+ | "alt+shift"
+ | "ctrl+alt"
+ | "ctrl+alt+shift";
-/* eslint-disable prettier/prettier */
-// prettier-ignore
export type Key =
- | "a"
- | "b"
- | "c"
- | "d"
- | "e"
- | "f"
- | "g"
- | "h"
- | "i"
- | "j"
- | "k"
- | "l"
- | "m"
- | "n"
- | "o"
- | "p"
- | "q"
- | "r"
- | "s"
- | "t"
- | "u"
- | "v"
- | "w"
- | "x"
- | "y"
- | "z"
- | "0"
- | "1"
- | "2"
- | "3"
- | "4"
- | "5"
- | "6"
- | "7"
- | "8"
- | "9"
- | "up"
- | "down"
- | "left"
- | "right"
- | "/"
- | "?"
- | "."
- | "enter"
- | "tab"
- | "space"
- | "escape"
- | "esc"
- | "backspace"
- | "delete"
- | "home"
- | "end";
+ | "a"
+ | "b"
+ | "c"
+ | "d"
+ | "e"
+ | "f"
+ | "g"
+ | "h"
+ | "i"
+ | "j"
+ | "k"
+ | "l"
+ | "m"
+ | "n"
+ | "o"
+ | "p"
+ | "q"
+ | "r"
+ | "s"
+ | "t"
+ | "u"
+ | "v"
+ | "w"
+ | "x"
+ | "y"
+ | "z"
+ | "0"
+ | "1"
+ | "2"
+ | "3"
+ | "4"
+ | "5"
+ | "6"
+ | "7"
+ | "8"
+ | "9"
+ | "up"
+ | "down"
+ | "left"
+ | "right"
+ | "/"
+ | "?"
+ | "."
+ | "enter"
+ | "tab"
+ | "space"
+ | "escape"
+ | "esc"
+ | "backspace"
+ | "delete"
+ | "home"
+ | "end";
/* eslint-enable */
export type ModifierBasedShortcutKey = `${ModifierKeys}+${Key}`;
@@ -77,5 +75,5 @@ export type SingleCharacterShortcutKey = `${Key}`;
export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey;
export type KeybindingConfig = {
- [key in ShortcutKey]?: ActionWithOptionalArgs;
+ [key in ShortcutKey]?: TActionWithOptionalArgs;
};
diff --git a/apps/web/src/types/language.ts b/apps/web/src/types/language.ts
new file mode 100644
index 00000000..a3f527c8
--- /dev/null
+++ b/apps/web/src/types/language.ts
@@ -0,0 +1,4 @@
+import type { LANGUAGES } from "@/constants/language-constants";
+
+export type Language = (typeof LANGUAGES)[number];
+export type LanguageCode = Language["code"];
diff --git a/apps/web/src/types/media.ts b/apps/web/src/types/media.ts
deleted file mode 100644
index 6e94fcff..00000000
--- a/apps/web/src/types/media.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export type MediaType = "image" | "video" | "audio";
-
-// What's stored in media library
-export interface MediaFile {
- id: string;
- name: string;
- type: MediaType;
- file: File;
- url?: string; // Object URL for preview
- thumbnailUrl?: string; // For video thumbnails
- duration?: number; // For video/audio duration
- width?: number; // For video/image width
- height?: number; // For video/image height
- fps?: number; // For video frame rate
- // Ephemeral items are used by timeline directly and should not appear in the media library or be persisted
- ephemeral?: boolean;
-}
diff --git a/apps/web/src/types/playback.ts b/apps/web/src/types/playback.ts
deleted file mode 100644
index 8758ea03..00000000
--- a/apps/web/src/types/playback.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-export interface PlaybackState {
- isPlaying: boolean;
- currentTime: number;
- duration: number;
- volume: number;
- speed: number;
- muted: boolean;
- previousVolume?: number;
-}
-
-export interface PlaybackControls {
- play: () => void;
- pause: () => void;
- seek: (time: number) => void;
- setVolume: (volume: number) => void;
- setSpeed: (speed: number) => void;
- toggle: () => void;
- mute: () => void;
- unmute: () => void;
- toggleMute: () => void;
-}
diff --git a/apps/web/src/types/post.ts b/apps/web/src/types/post.ts
deleted file mode 100644
index fbee239e..00000000
--- a/apps/web/src/types/post.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-export type Post = {
- id: string;
- slug: string;
- title: string;
- content: string;
- description: string;
- coverImage: string;
- publishedAt: Date;
- updatedAt: Date;
- authors: {
- id: string;
- name: string;
- image: string;
- }[];
- category: {
- id: string;
- slug: string;
- name: string;
- };
- tags: {
- id: string;
- slug: string;
- name: string;
- }[];
- attribution: {
- author: string;
- url: string;
- } | null;
-};
-
-export type Pagination = {
- limit: number;
- currpage: number;
- nextPage: number | null;
- prevPage: number | null;
- totalItems: number;
- totalPages: number;
-};
-
-export type MarblePostList = {
- posts: Post[];
- pagination: Pagination;
-};
-
-export type MarblePost = {
- post: Post;
-};
-
-export type Tag = {
- id: string;
- name: string;
- slug: string;
-};
-
-export type MarbleTag = {
- tag: Tag;
-};
-
-export type MarbleTagList = {
- tags: Tag[];
- pagination: Pagination;
-};
-
-export type Category = {
- id: string;
- name: string;
- slug: string;
-};
-
-export type MarbleCategory = {
- category: Category;
-};
-
-export type MarbleCategoryList = {
- categories: Category[];
- pagination: Pagination;
-};
-
-export type Author = {
- id: string;
- name: string;
- image: string;
-};
-
-export type MarbleAuthor = {
- author: Author;
-};
-
-export type MarbleAuthorList = {
- authors: Author[];
- pagination: Pagination;
-};
diff --git a/apps/web/src/types/project.ts b/apps/web/src/types/project.ts
index f182653d..c8074c26 100644
--- a/apps/web/src/types/project.ts
+++ b/apps/web/src/types/project.ts
@@ -1,35 +1,51 @@
-import { CanvasSize } from "./editor";
+import type { TScene } from "./timeline";
-export type BlurIntensity = 4 | 8 | 18;
+export type TBackground =
+ | {
+ type: "color";
+ color: string;
+ }
+ | {
+ type: "blur";
+ blurIntensity: number;
+ };
-export interface Scene {
- id: string;
- name: string;
- isMain: boolean;
- createdAt: Date;
- updatedAt: Date;
+export interface TCanvasSize {
+ width: number;
+ height: number;
+}
+
+export interface TProjectMetadata {
+ id: string;
+ name: string;
+ thumbnail?: string;
+ duration: number;
+ createdAt: Date;
+ updatedAt: Date;
+}
+
+export interface TProjectSettings {
+ fps: number;
+ canvasSize: TCanvasSize;
+ originalCanvasSize?: TCanvasSize | null;
+ background: TBackground;
+}
+
+export interface TTimelineViewState {
+ zoomLevel: number;
+ scrollLeft: number;
+ playheadTime: number;
}
export interface TProject {
- id: string;
- name: string;
- thumbnail: string;
- createdAt: Date;
- updatedAt: Date;
- scenes: Scene[];
- currentSceneId: string;
- mediaItems?: string[];
- backgroundColor?: string;
- backgroundType?: "color" | "blur";
- blurIntensity?: BlurIntensity;
- fps?: number;
- bookmarks?: number[];
- canvasSize: CanvasSize;
- /**
- * Tracks how the canvas size was set:
- * - "preset": User selected a standard preset (e.g. 16:9, 9:16)
- * - "original": Using the first media item's dimensions
- * - "custom": User set a custom aspect ratio or dimensions
- */
- canvasMode: "preset" | "original" | "custom";
+ metadata: TProjectMetadata;
+ scenes: TScene[];
+ currentSceneId: string;
+ settings: TProjectSettings;
+ version: number;
+ timelineViewState?: TTimelineViewState;
}
+
+export type TProjectSortKey = "createdAt" | "updatedAt" | "name" | "duration";
+export type TSortOrder = "asc" | "desc";
+export type TProjectSortOption = `${TProjectSortKey}-${TSortOrder}`;
diff --git a/apps/web/src/types/sounds.ts b/apps/web/src/types/sounds.ts
index faa485eb..f7526846 100644
--- a/apps/web/src/types/sounds.ts
+++ b/apps/web/src/types/sounds.ts
@@ -1,39 +1,39 @@
export interface SoundEffect {
- id: number;
- name: string;
- description: string;
- url: string;
- previewUrl?: string;
- downloadUrl?: string;
- duration: number;
- filesize: number;
- type: string;
- channels: number;
- bitrate: number;
- bitdepth: number;
- samplerate: number;
- username: string;
- tags: string[];
- license: string;
- created: string;
- downloads: number;
- rating: number;
- ratingCount: number;
+ id: number;
+ name: string;
+ description: string;
+ url: string;
+ previewUrl?: string;
+ downloadUrl?: string;
+ duration: number;
+ filesize: number;
+ type: string;
+ channels: number;
+ bitrate: number;
+ bitdepth: number;
+ samplerate: number;
+ username: string;
+ tags: string[];
+ license: string;
+ created: string;
+ downloads: number;
+ rating: number;
+ ratingCount: number;
}
export interface SavedSound {
- id: number; // freesound id
- name: string;
- username: string;
- previewUrl?: string;
- downloadUrl?: string;
- duration: number;
- tags: string[];
- license: string;
- savedAt: string; // iso date string
+ id: number; // freesound id
+ name: string;
+ username: string;
+ previewUrl?: string;
+ downloadUrl?: string;
+ duration: number;
+ tags: string[];
+ license: string;
+ savedAt: string; // iso date string
}
export interface SavedSoundsData {
- sounds: SavedSound[];
- lastModified: string;
+ sounds: SavedSound[];
+ lastModified: string;
}
diff --git a/apps/web/src/types/stickers.ts b/apps/web/src/types/stickers.ts
new file mode 100644
index 00000000..c6adfd7a
--- /dev/null
+++ b/apps/web/src/types/stickers.ts
@@ -0,0 +1,3 @@
+import type { STICKER_CATEGORIES } from "@/constants/stickers-constants";
+
+export type StickerCategory = (typeof STICKER_CATEGORIES)[number];
diff --git a/apps/web/src/types/time.ts b/apps/web/src/types/time.ts
new file mode 100644
index 00000000..4009a1e1
--- /dev/null
+++ b/apps/web/src/types/time.ts
@@ -0,0 +1 @@
+export type TTimeCode = "MM:SS" | "HH:MM:SS" | "HH:MM:SS:CS" | "HH:MM:SS:FF";
diff --git a/apps/web/src/types/timeline.ts b/apps/web/src/types/timeline.ts
index 61ba6af0..69d40518 100644
--- a/apps/web/src/types/timeline.ts
+++ b/apps/web/src/types/timeline.ts
@@ -1,166 +1,190 @@
-import { MediaType } from "@/types/media";
-import { generateUUID } from "@/lib/utils";
+export interface TScene {
+ id: string;
+ name: string;
+ isMain: boolean;
+ tracks: TimelineTrack[];
+ bookmarks: number[];
+ createdAt: Date;
+ updatedAt: Date;
+}
-export type TrackType = "media" | "text" | "audio";
+export type TrackType = "video" | "text" | "audio" | "sticker";
-// Base element properties
-interface BaseTimelineElement {
- id: string;
- name: string;
- duration: number;
- startTime: number;
- trimStart: number;
- trimEnd: number;
- hidden?: boolean;
+interface BaseTrack {
+ id: string;
+ name: string;
}
-// Media element that references MediaStore
-export interface MediaElement extends BaseTimelineElement {
- type: "media";
- mediaId: string;
- muted?: boolean;
+export interface VideoTrack extends BaseTrack {
+ type: "video";
+ elements: (VideoElement | ImageElement)[];
+ isMain: boolean;
+ muted: boolean;
+ hidden: boolean;
}
-// Text element with embedded text data
-export interface TextElement extends BaseTimelineElement {
- type: "text";
- content: string;
- fontSize: number;
- fontFamily: string;
- color: string;
- backgroundColor: string;
- textAlign: "left" | "center" | "right";
- fontWeight: "normal" | "bold";
- fontStyle: "normal" | "italic";
- textDecoration: "none" | "underline" | "line-through";
- x: number; // Position relative to canvas center
- y: number; // Position relative to canvas center
- rotation: number; // in degrees
- opacity: number; // 0-1
+export interface TextTrack extends BaseTrack {
+ type: "text";
+ elements: TextElement[];
+ hidden: boolean;
}
-// Typed timeline elements
-export type TimelineElement = MediaElement | TextElement;
+export interface AudioTrack extends BaseTrack {
+ type: "audio";
+ elements: AudioElement[];
+ muted: boolean;
+}
-// Creation types (without id, for addElementToTrack)
-export type CreateMediaElement = Omit<MediaElement, "id">;
-export type CreateTextElement = Omit<TextElement, "id">;
-export type CreateTimelineElement = CreateMediaElement | CreateTextElement;
+export interface StickerTrack extends BaseTrack {
+ type: "sticker";
+ elements: StickerElement[];
+ hidden: boolean;
+}
+
+export type TimelineTrack = VideoTrack | TextTrack | AudioTrack | StickerTrack;
-export interface TimelineElementProps {
- element: TimelineElement;
- track: TimelineTrack;
- zoomLevel: number;
- isSelected: boolean;
- onElementMouseDown: (e: React.MouseEvent, element: TimelineElement) => void;
- onElementClick: (e: React.MouseEvent, element: TimelineElement) => void;
+export interface Transform {
+ scale: number;
+ position: {
+ x: number;
+ y: number;
+ };
+ rotate: number;
}
-export interface ResizeState {
- elementId: string;
- side: "left" | "right";
- startX: number;
- initialTrimStart: number;
- initialTrimEnd: number;
+interface BaseAudioElement extends BaseTimelineElement {
+ type: "audio";
+ volume: number;
+ muted?: boolean;
+ buffer?: AudioBuffer;
}
-// Drag data types for type-safe drag and drop
-export interface MediaItemDragData {
- id: string;
- type: MediaType;
- name: string;
+export interface UploadAudioElement extends BaseAudioElement {
+ sourceType: "upload";
+ mediaId: string;
}
-export interface TextItemDragData {
- id: string;
- type: "text";
- name: string;
- content: string;
+export interface LibraryAudioElement extends BaseAudioElement {
+ sourceType: "library";
+ sourceUrl: string;
}
-export type DragData = MediaItemDragData | TextItemDragData;
+export type AudioElement = UploadAudioElement | LibraryAudioElement;
-export interface TimelineTrack {
- id: string;
- name: string;
- type: TrackType;
- elements: TimelineElement[];
- muted?: boolean;
- isMain?: boolean;
+interface BaseTimelineElement {
+ id: string;
+ name: string;
+ duration: number;
+ startTime: number;
+ trimStart: number;
+ trimEnd: number;
}
-export function sortTracksByOrder(tracks: TimelineTrack[]): TimelineTrack[] {
- return [...tracks].sort((a, b) => {
- // Text tracks always go to the top
- if (a.type === "text" && b.type !== "text") return -1;
- if (b.type === "text" && a.type !== "text") return 1;
-
- // Audio tracks always go to bottom
- if (a.type === "audio" && b.type !== "audio") return 1;
- if (b.type === "audio" && a.type !== "audio") return -1;
+export interface VideoElement extends BaseTimelineElement {
+ type: "video";
+ mediaId: string;
+ muted?: boolean;
+ hidden?: boolean;
+ transform: Transform;
+ opacity: number;
+}
- // Main track goes above audio but below text tracks
- if (a.isMain && !b.isMain && b.type !== "audio" && b.type !== "text")
- return 1;
- if (b.isMain && !a.isMain && a.type !== "audio" && a.type !== "text")
- return -1;
+export interface ImageElement extends BaseTimelineElement {
+ type: "image";
+ mediaId: string;
+ hidden?: boolean;
+ transform: Transform;
+ opacity: number;
+}
- // Within same category, maintain creation order
- return 0;
- });
+export interface TextElement extends BaseTimelineElement {
+ type: "text";
+ content: string;
+ fontSize: number;
+ fontFamily: string;
+ color: string;
+ backgroundColor: string;
+ textAlign: "left" | "center" | "right";
+ fontWeight: "normal" | "bold";
+ fontStyle: "normal" | "italic";
+ textDecoration: "none" | "underline" | "line-through";
+ hidden?: boolean;
+ transform: Transform;
+ opacity: number;
}
-export function getMainTrack(tracks: TimelineTrack[]): TimelineTrack | null {
- return tracks.find((track) => track.isMain) || null;
+export interface StickerElement extends BaseTimelineElement {
+ type: "sticker";
+ iconName: string;
+ hidden?: boolean;
+ transform: Transform;
+ opacity: number;
+ color?: string;
}
-export function ensureMainTrack(tracks: TimelineTrack[]): TimelineTrack[] {
- const hasMainTrack = tracks.some((track) => track.isMain);
+export type TimelineElement =
+ | AudioElement
+ | VideoElement
+ | ImageElement
+ | TextElement
+ | StickerElement;
- if (!hasMainTrack) {
- // Create main track if it doesn't exist
- const mainTrack: TimelineTrack = {
- id: generateUUID(),
- name: "Main Track",
- type: "media",
- elements: [],
- muted: false,
- isMain: true,
- };
- return [mainTrack, ...tracks];
- }
+export type ElementType = TimelineElement["type"];
- return tracks;
-}
+export type CreateUploadAudioElement = Omit<UploadAudioElement, "id">;
+export type CreateLibraryAudioElement = Omit<LibraryAudioElement, "id">;
+export type CreateAudioElement =
+ | CreateUploadAudioElement
+ | CreateLibraryAudioElement;
+export type CreateVideoElement = Omit<VideoElement, "id">;
+export type CreateImageElement = Omit<ImageElement, "id">;
+export type CreateTextElement = Omit<TextElement, "id">;
+export type CreateStickerElement = Omit<StickerElement, "id">;
+export type CreateTimelineElement =
+ | CreateAudioElement
+ | CreateVideoElement
+ | CreateImageElement
+ | CreateTextElement
+ | CreateStickerElement;
-// Timeline validation utilities
-export function canElementGoOnTrack(
- elementType: "text" | "media",
- trackType: TrackType
-): boolean {
- if (elementType === "text") {
- return trackType === "text";
- }
- if (elementType === "media") {
- return trackType === "media" || trackType === "audio";
- }
- return false;
-}
+// ---- Drag State ----
-export function validateElementTrackCompatibility(
- element: { type: "text" | "media" },
- track: { type: TrackType }
-): { isValid: boolean; errorMessage?: string } {
- const isValid = canElementGoOnTrack(element.type, track.type);
+export interface ElementDragState {
+ isDragging: boolean;
+ elementId: string | null;
+ trackId: string | null;
+ startMouseX: number;
+ startMouseY: number;
+ startElementTime: number;
+ clickOffsetTime: number;
+ currentTime: number;
+ currentMouseY: number;
+}
- if (!isValid) {
- const errorMessage =
- element.type === "text"
- ? "Text elements can only be placed on text tracks"
- : "Media elements can only be placed on media or audio tracks";
+export interface DropTarget {
+ trackIndex: number;
+ isNewTrack: boolean;
+ insertPosition: "above" | "below" | null;
+ xPosition: number;
+}
- return { isValid: false, errorMessage };
- }
+export interface ComputeDropTargetParams {
+ elementType: ElementType;
+ mouseX: number;
+ mouseY: number;
+ tracks: TimelineTrack[];
+ playheadTime: number;
+ isExternalDrop: boolean;
+ elementDuration: number;
+ pixelsPerSecond: number;
+ zoomLevel: number;
+ verticalDragDirection?: "up" | "down" | null;
+ startTimeOverride?: number;
+ excludeElementId?: string;
+}
- return { isValid: true };
+export interface ClipboardItem {
+ trackId: string;
+ trackType: TrackType;
+ element: CreateTimelineElement;
}
diff --git a/apps/web/src/types/transcription.ts b/apps/web/src/types/transcription.ts
new file mode 100644
index 00000000..f189c6c9
--- /dev/null
+++ b/apps/web/src/types/transcription.ts
@@ -0,0 +1,47 @@
+import type { LanguageCode } from "./language";
+
+export type TranscriptionLanguage = LanguageCode | "auto";
+
+export interface TranscriptionSegment {
+ text: string;
+ start: number;
+ end: number;
+}
+
+export interface TranscriptionResult {
+ text: string;
+ segments: TranscriptionSegment[];
+ language: string;
+}
+
+export type TranscriptionStatus =
+ | "idle"
+ | "loading-model"
+ | "transcribing"
+ | "complete"
+ | "error";
+
+export interface TranscriptionProgress {
+ status: TranscriptionStatus;
+ progress: number;
+ message?: string;
+}
+
+export type TranscriptionModelId =
+ | "whisper-tiny"
+ | "whisper-small"
+ | "whisper-medium"
+ | "whisper-large-v3-turbo";
+
+export interface TranscriptionModel {
+ id: TranscriptionModelId;
+ name: string;
+ huggingFaceId: string;
+ description: string;
+}
+
+export interface CaptionChunk {
+ text: string;
+ startTime: number;
+ duration: number;
+}