summaryrefslogtreecommitdiff
path: root/apps/web/src/types
diff options
context:
space:
mode:
authorSimon Orzel <simonorzel@gmail.com>2025-07-20 18:35:06 +0200
committerSimon Orzel <simonorzel@gmail.com>2025-07-20 18:35:06 +0200
commitb03287df8d6fa14153af264c6a3e3da5b2a06153 (patch)
tree100b2c2a729295419dce61655bca418e74195ec4 /apps/web/src/types
parent061624ab588afff18073ae62fbac425c1aa5cf3a (diff)
parentba3da43728bc051aa033dc7b45042b0f0f788e63 (diff)
downloadopencut-b03287df8d6fa14153af264c6a3e3da5b2a06153.tar.gz
Merge branch 'main' into feat/project-preview-images
Diffstat (limited to 'apps/web/src/types')
-rw-r--r--apps/web/src/types/editor.ts24
-rw-r--r--apps/web/src/types/keybinding.ts74
-rw-r--r--apps/web/src/types/playback.ts32
-rw-r--r--apps/web/src/types/project.ts20
-rw-r--r--apps/web/src/types/timeline.ts180
5 files changed, 142 insertions, 188 deletions
diff --git a/apps/web/src/types/editor.ts b/apps/web/src/types/editor.ts
index d09e1912..dce7ac50 100644
--- a/apps/web/src/types/editor.ts
+++ b/apps/web/src/types/editor.ts
@@ -1,12 +1,12 @@
-export type BackgroundType = "blur" | "mirror" | "color";
-
-export interface CanvasSize {
- width: number;
- height: number;
-}
-
-export interface CanvasPreset {
- name: string;
- width: number;
- height: number;
-}
+export type BackgroundType = "blur" | "mirror" | "color";
+
+export interface CanvasSize {
+ width: number;
+ height: number;
+}
+
+export interface CanvasPreset {
+ name: string;
+ width: number;
+ height: number;
+}
diff --git a/apps/web/src/types/keybinding.ts b/apps/web/src/types/keybinding.ts
index c66a04dd..d97c88da 100644
--- a/apps/web/src/types/keybinding.ts
+++ b/apps/web/src/types/keybinding.ts
@@ -5,69 +5,23 @@ import { ActionWithOptionalArgs } from "@/constants/actions";
* 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 +31,5 @@ export type SingleCharacterShortcutKey = `${Key}`;
export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey;
export type KeybindingConfig = {
- [key in ShortcutKey]?: ActionWithOptionalArgs;
+ [key in ShortcutKey]?: ActionWithOptionalArgs;
};
diff --git a/apps/web/src/types/playback.ts b/apps/web/src/types/playback.ts
index 18c1e099..8758ea03 100644
--- a/apps/web/src/types/playback.ts
+++ b/apps/web/src/types/playback.ts
@@ -1,21 +1,21 @@
export interface PlaybackState {
- isPlaying: boolean;
- currentTime: number;
- duration: number;
- volume: number;
- speed: number;
- muted: boolean;
- previousVolume?: number;
+ 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;
+ 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/project.ts b/apps/web/src/types/project.ts
index b3ab1332..58d37b03 100644
--- a/apps/web/src/types/project.ts
+++ b/apps/web/src/types/project.ts
@@ -1,12 +1,12 @@
export interface TProject {
- id: string;
- name: string;
- thumbnail: string;
- createdAt: Date;
- updatedAt: Date;
- mediaItems?: string[];
- backgroundColor?: string;
- backgroundType?: "color" | "blur";
- blurIntensity?: number; // in pixels (4, 8, 18)
- fps?: number;
+ id: string;
+ name: string;
+ thumbnail: string;
+ createdAt: Date;
+ updatedAt: Date;
+ mediaItems?: string[];
+ backgroundColor?: string;
+ backgroundType?: "color" | "blur";
+ blurIntensity?: number; // in pixels (4, 8, 18)
+ fps?: number;
}
diff --git a/apps/web/src/types/timeline.ts b/apps/web/src/types/timeline.ts
index e45908d6..17dbb5d1 100644
--- a/apps/web/src/types/timeline.ts
+++ b/apps/web/src/types/timeline.ts
@@ -5,36 +5,36 @@ export type TrackType = "media" | "text" | "audio";
// Base element properties
interface BaseTimelineElement {
- id: string;
- name: string;
- duration: number;
- startTime: number;
- trimStart: number;
- trimEnd: number;
+ id: string;
+ name: string;
+ duration: number;
+ startTime: number;
+ trimStart: number;
+ trimEnd: number;
}
// Media element that references MediaStore
export interface MediaElement extends BaseTimelineElement {
- type: "media";
- mediaId: string;
+ type: "media";
+ mediaId: string;
}
// 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
+ 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
}
// Typed timeline elements
@@ -46,112 +46,112 @@ export type CreateTextElement = Omit<TextElement, "id">;
export type CreateTimelineElement = CreateMediaElement | CreateTextElement;
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;
+ element: TimelineElement;
+ track: TimelineTrack;
+ zoomLevel: number;
+ isSelected: boolean;
+ onElementMouseDown: (e: React.MouseEvent, element: TimelineElement) => void;
+ onElementClick: (e: React.MouseEvent, element: TimelineElement) => void;
}
export interface ResizeState {
- elementId: string;
- side: "left" | "right";
- startX: number;
- initialTrimStart: number;
- initialTrimEnd: number;
+ elementId: string;
+ side: "left" | "right";
+ startX: number;
+ initialTrimStart: number;
+ initialTrimEnd: number;
}
// Drag data types for type-safe drag and drop
export interface MediaItemDragData {
- id: string;
- type: MediaType;
- name: string;
+ id: string;
+ type: MediaType;
+ name: string;
}
export interface TextItemDragData {
- id: string;
- type: "text";
- name: string;
- content: string;
+ id: string;
+ type: "text";
+ name: string;
+ content: string;
}
export type DragData = MediaItemDragData | TextItemDragData;
export interface TimelineTrack {
- id: string;
- name: string;
- type: TrackType;
- elements: TimelineElement[];
- muted?: boolean;
- isMain?: boolean;
+ id: string;
+ name: string;
+ type: TrackType;
+ elements: TimelineElement[];
+ muted?: boolean;
+ isMain?: boolean;
}
export function sortTracksByOrder(tracks: TimelineTrack[]): TimelineTrack[] {
- return [...tracks].sort((a, b) => {
- // Audio tracks always go to bottom
- if (a.type === "audio" && b.type !== "audio") return 1;
- if (b.type === "audio" && a.type !== "audio") return -1;
+ return [...tracks].sort((a, b) => {
+ // Audio tracks always go to bottom
+ if (a.type === "audio" && b.type !== "audio") return 1;
+ if (b.type === "audio" && a.type !== "audio") return -1;
- // Main track goes above audio but below other tracks
- if (a.isMain && !b.isMain && b.type !== "audio") return 1;
- if (b.isMain && !a.isMain && a.type !== "audio") return -1;
+ // Main track goes above audio but below other tracks
+ if (a.isMain && !b.isMain && b.type !== "audio") return 1;
+ if (b.isMain && !a.isMain && a.type !== "audio") return -1;
- // Within same category, maintain creation order
- return 0;
- });
+ // Within same category, maintain creation order
+ return 0;
+ });
}
export function getMainTrack(tracks: TimelineTrack[]): TimelineTrack | null {
- return tracks.find((track) => track.isMain) || null;
+ return tracks.find((track) => track.isMain) || null;
}
export function ensureMainTrack(tracks: TimelineTrack[]): TimelineTrack[] {
- const hasMainTrack = tracks.some((track) => track.isMain);
+ const hasMainTrack = tracks.some((track) => track.isMain);
- 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];
- }
+ 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];
+ }
- return tracks;
+ return tracks;
}
// Timeline validation utilities
export function canElementGoOnTrack(
- elementType: "text" | "media",
- trackType: TrackType,
+ elementType: "text" | "media",
+ trackType: TrackType
): boolean {
- if (elementType === "text") {
- return trackType === "text";
- } else if (elementType === "media") {
- return trackType === "media" || trackType === "audio";
- }
- return false;
+ if (elementType === "text") {
+ return trackType === "text";
+ } else if (elementType === "media") {
+ return trackType === "media" || trackType === "audio";
+ }
+ return false;
}
export function validateElementTrackCompatibility(
- element: { type: "text" | "media" },
- track: { type: TrackType },
+ element: { type: "text" | "media" },
+ track: { type: TrackType }
): { isValid: boolean; errorMessage?: string } {
- const isValid = canElementGoOnTrack(element.type, track.type);
+ const isValid = canElementGoOnTrack(element.type, track.type);
- 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";
+ 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";
- return { isValid: false, errorMessage };
- }
+ return { isValid: false, errorMessage };
+ }
- return { isValid: true };
+ return { isValid: true };
}