@@ -1121,6 +1182,8 @@ function TimelineToolbar({
+
+
+ {element.hidden && (
+
+ {isAudio ? (
+
+ ) : (
+
+ )}
+
+ )}
+
{isSelected && (
<>
Split at playhead
+
+ {isAudio ? (
+ element.hidden ? (
+
+ ) : (
+
+ )
+ ) : element.hidden ? (
+
+ ) : (
+
+ )}
+
+ {isAudio
+ ? element.hidden
+ ? "Unmute"
+ : "Mute"
+ : element.hidden
+ ? "Show"
+ : "Hide"}{" "}
+ {element.type === "text" ? "text" : "clip"}
+
+
Duplicate {element.type === "text" ? "text" : "clip"}
diff --git a/apps/web/src/lib/storage/storage-service.ts b/apps/web/src/lib/storage/storage-service.ts
index 0037dbdb..2ff63ac7 100644
--- a/apps/web/src/lib/storage/storage-service.ts
+++ b/apps/web/src/lib/storage/storage-service.ts
@@ -63,6 +63,8 @@ class StorageService {
backgroundColor: project.backgroundColor,
backgroundType: project.backgroundType,
blurIntensity: project.blurIntensity,
+ bookmarks: project.bookmarks,
+ fps: project.fps,
};
await this.projectsAdapter.set(project.id, serializedProject);
@@ -83,6 +85,8 @@ class StorageService {
backgroundColor: serializedProject.backgroundColor,
backgroundType: serializedProject.backgroundType,
blurIntensity: serializedProject.blurIntensity,
+ bookmarks: serializedProject.bookmarks,
+ fps: serializedProject.fps,
};
}
diff --git a/apps/web/src/lib/storage/types.ts b/apps/web/src/lib/storage/types.ts
index b662c164..0cfafd47 100644
--- a/apps/web/src/lib/storage/types.ts
+++ b/apps/web/src/lib/storage/types.ts
@@ -37,6 +37,7 @@ export interface StorageConfig {
export type SerializedProject = Omit & {
createdAt: string;
updatedAt: string;
+ bookmarks?: number[];
};
// Extend FileSystemDirectoryHandle with missing async iterator methods
diff --git a/apps/web/src/stores/project-store.ts b/apps/web/src/stores/project-store.ts
index 7bb38039..69aca3ec 100644
--- a/apps/web/src/stores/project-store.ts
+++ b/apps/web/src/stores/project-store.ts
@@ -27,6 +27,11 @@ interface ProjectStore {
options?: { backgroundColor?: string; blurIntensity?: number }
) => Promise;
updateProjectFps: (fps: number) => Promise;
+
+ // Bookmark methods
+ toggleBookmark: (time: number) => Promise;
+ isBookmarked: (time: number) => boolean;
+ removeBookmark: (time: number) => Promise;
getFilteredAndSortedProjects: (
searchQuery: string,
@@ -39,6 +44,97 @@ export const useProjectStore = create((set, get) => ({
savedProjects: [],
isLoading: true,
isInitialized: false,
+
+ // Implementation of bookmark methods
+ toggleBookmark: async (time: number) => {
+ const { activeProject } = get();
+ if (!activeProject) return;
+
+ // Round time to the nearest frame
+ const fps = activeProject.fps || 30;
+ const frameTime = Math.round(time * fps) / fps;
+
+ const bookmarks = activeProject.bookmarks || [];
+ let updatedBookmarks: number[];
+
+ // Check if already bookmarked
+ const bookmarkIndex = bookmarks.findIndex(
+ bookmark => Math.abs(bookmark - frameTime) < 0.001
+ );
+
+ if (bookmarkIndex !== -1) {
+ // Remove bookmark
+ updatedBookmarks = bookmarks.filter((_, i) => i !== bookmarkIndex);
+ } else {
+ // Add bookmark
+ updatedBookmarks = [...bookmarks, frameTime].sort((a, b) => a - b);
+ }
+
+ const updatedProject = {
+ ...activeProject,
+ bookmarks: updatedBookmarks,
+ updatedAt: new Date(),
+ };
+
+ try {
+ await storageService.saveProject(updatedProject);
+ set({ activeProject: updatedProject });
+ await get().loadAllProjects(); // Refresh the list
+ } catch (error) {
+ console.error("Failed to update project bookmarks:", error);
+ toast.error("Failed to update bookmarks", {
+ description: "Please try again",
+ });
+ }
+ },
+
+ isBookmarked: (time: number) => {
+ const { activeProject } = get();
+ if (!activeProject || !activeProject.bookmarks) return false;
+
+ // Round time to the nearest frame
+ const fps = activeProject.fps || 30;
+ const frameTime = Math.round(time * fps) / fps;
+
+ return activeProject.bookmarks.some(
+ bookmark => Math.abs(bookmark - frameTime) < 0.001
+ );
+ },
+
+ removeBookmark: async (time: number) => {
+ const { activeProject } = get();
+ if (!activeProject || !activeProject.bookmarks) return;
+
+ // Round time to the nearest frame
+ const fps = activeProject.fps || 30;
+ const frameTime = Math.round(time * fps) / fps;
+
+ const updatedBookmarks = activeProject.bookmarks.filter(
+ bookmark => Math.abs(bookmark - frameTime) >= 0.001
+ );
+
+ if (updatedBookmarks.length === activeProject.bookmarks.length) {
+ // No bookmark found to remove
+ return;
+ }
+
+ const updatedProject = {
+ ...activeProject,
+ bookmarks: updatedBookmarks,
+ updatedAt: new Date(),
+ };
+
+ try {
+ await storageService.saveProject(updatedProject);
+ set({ activeProject: updatedProject });
+ await get().loadAllProjects(); // Refresh the list
+ } catch (error) {
+ console.error("Failed to update project bookmarks:", error);
+ toast.error("Failed to remove bookmark", {
+ description: "Please try again",
+ });
+ }
+ },
createNewProject: async (name: string) => {
const newProject: TProject = {
@@ -50,6 +146,7 @@ export const useProjectStore = create((set, get) => ({
backgroundColor: "#000000",
backgroundType: "color",
blurIntensity: 8,
+ bookmarks: [],
};
set({ activeProject: newProject });
diff --git a/apps/web/src/stores/timeline-store.ts b/apps/web/src/stores/timeline-store.ts
index f3497be9..0f441176 100644
--- a/apps/web/src/stores/timeline-store.ts
+++ b/apps/web/src/stores/timeline-store.ts
@@ -126,6 +126,7 @@ interface TimelineStore {
pushHistory?: boolean
) => void;
toggleTrackMute: (trackId: string) => void;
+ toggleElementHidden: (trackId: string, elementId: string) => void;
// Split operations for elements
splitElement: (
@@ -868,6 +869,24 @@ export const useTimelineStore = create((set, get) => {
);
},
+ toggleElementHidden: (trackId, elementId) => {
+ get().pushHistory();
+ updateTracksAndSave(
+ get()._tracks.map((track) =>
+ track.id === trackId
+ ? {
+ ...track,
+ elements: track.elements.map((element) =>
+ element.id === elementId
+ ? { ...element, hidden: !element.hidden }
+ : element
+ ),
+ }
+ : track
+ )
+ );
+ },
+
updateTextElement: (trackId, elementId, updates) => {
get().pushHistory();
updateTracksAndSave(
diff --git a/apps/web/src/types/project.ts b/apps/web/src/types/project.ts
index 58d37b03..f52cba73 100644
--- a/apps/web/src/types/project.ts
+++ b/apps/web/src/types/project.ts
@@ -9,4 +9,5 @@ export interface TProject {
backgroundType?: "color" | "blur";
blurIntensity?: number; // in pixels (4, 8, 18)
fps?: number;
+ bookmarks?: number[];
}
diff --git a/apps/web/src/types/timeline.ts b/apps/web/src/types/timeline.ts
index ef20c642..cd17b7dd 100644
--- a/apps/web/src/types/timeline.ts
+++ b/apps/web/src/types/timeline.ts
@@ -11,6 +11,7 @@ interface BaseTimelineElement {
startTime: number;
trimStart: number;
trimEnd: number;
+ hidden?: boolean;
}
// Media element that references MediaStore
--
cgit v1.3.1