feat: add hide or show for media elements, mute or unmute for audio elements (#500)

* feat: create external tools section (#493)

* feat: create external tools section

* fix: better wording

* Feature, added hide or show for media elements, mute or unmute for audio elements both effective on timeline and preview panel, with overlay icon for indicating so and state storing,

* Minor UI vertical separator between magnifier and other buttons

* fixed the AbortError issue in the AudioWaveform component. The error was occurring because of a race condition during cleanup when the component unmounts.

* feat: implement bookmarking functionality in the timeline and project storage

- Added bookmark management methods in the project store for toggling and checking bookmarks.
- Updated the timeline component to display bookmark markers and integrate bookmark actions in the context menu.
- Enhanced the storage service to handle bookmarks in project serialization and deserialization.
- Updated project types to include bookmarks as an array of numbers.

---------

Co-authored-by: Dominik K. <dominik@koch-bautechnik.de>
Co-authored-by: Maze Winther <mazewinther@gmail.com>
This commit is contained in:
melad
2025-07-31 12:50:22 +02:00
committed by GitHub
co-authored by Dominik K. Maze Winther
parent b229d94a8d
commit f255ccb818
10 changed files with 302 additions and 21 deletions
+97
View File
@@ -27,6 +27,11 @@ interface ProjectStore {
options?: { backgroundColor?: string; blurIntensity?: number }
) => Promise<void>;
updateProjectFps: (fps: number) => Promise<void>;
// Bookmark methods
toggleBookmark: (time: number) => Promise<void>;
isBookmarked: (time: number) => boolean;
removeBookmark: (time: number) => Promise<void>;
getFilteredAndSortedProjects: (
searchQuery: string,
@@ -39,6 +44,97 @@ export const useProjectStore = create<ProjectStore>((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<ProjectStore>((set, get) => ({
backgroundColor: "#000000",
backgroundType: "color",
blurIntensity: 8,
bookmarks: [],
};
set({ activeProject: newProject });
+19
View File
@@ -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<TimelineStore>((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(