summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx')
-rw-r--r--apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx116
1 files changed, 116 insertions, 0 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx b/apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx
new file mode 100644
index 00000000..f6494738
--- /dev/null
+++ b/apps/web/src/components/editor/timeline/timeline-cache-indicator.tsx
@@ -0,0 +1,116 @@
+"use client";
+
+import { cn } from "@/lib/utils";
+import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
+import { TimelineTrack } from "@/types/timeline";
+import { MediaFile } from "@/types/media";
+import { TProject } from "@/types/project";
+
+interface CacheSegment {
+ startTime: number;
+ endTime: number;
+ cached: boolean;
+}
+
+interface TimelineCacheIndicatorProps {
+ duration: number;
+ zoomLevel: number;
+ tracks: TimelineTrack[];
+ mediaFiles: MediaFile[];
+ activeProject: TProject | null;
+ getRenderStatus: (
+ time: number,
+ tracks: TimelineTrack[],
+ mediaFiles: MediaFile[],
+ activeProject: TProject | null
+ ) => "cached" | "not-cached";
+}
+
+export function TimelineCacheIndicator({
+ duration,
+ zoomLevel,
+ tracks,
+ mediaFiles,
+ activeProject,
+ getRenderStatus,
+}: TimelineCacheIndicatorProps) {
+ // Calculate cache segments by sampling the timeline
+ const calculateCacheSegments = (): CacheSegment[] => {
+ const segments: CacheSegment[] = [];
+ const sampleRate = 10; // Sample every 0.1 seconds
+ const totalSamples = Math.ceil(duration * sampleRate);
+
+ if (totalSamples === 0) {
+ return [{ startTime: 0, endTime: duration, cached: false }];
+ }
+
+ let currentSegment: CacheSegment | null = null;
+
+ for (let i = 0; i <= totalSamples; i++) {
+ const time = i / sampleRate;
+ const cached =
+ getRenderStatus(time, tracks, mediaFiles, activeProject) === "cached";
+
+ if (!currentSegment) {
+ // Start first segment
+ currentSegment = {
+ startTime: time,
+ endTime: time,
+ cached,
+ };
+ } else if (currentSegment.cached === cached) {
+ // Extend current segment
+ currentSegment.endTime = time;
+ } else {
+ // Finish current segment and start new one
+ segments.push(currentSegment);
+ currentSegment = {
+ startTime: time,
+ endTime: time,
+ cached,
+ };
+ }
+ }
+
+ // Add the last segment
+ if (currentSegment) {
+ currentSegment.endTime = duration;
+ segments.push(currentSegment);
+ }
+
+ return segments;
+ };
+
+ const cacheSegments = calculateCacheSegments();
+
+ return (
+ <div className="absolute top-0 left-0 right-0 h-px z-10 pointer-events-none">
+ {cacheSegments.map((segment, index) => {
+ const startX =
+ segment.startTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
+ const endX =
+ segment.endTime * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
+ const width = endX - startX;
+
+ return (
+ <div
+ key={index}
+ className={cn(
+ "absolute top-0 h-px",
+ segment.cached ? "bg-primary" : "bg-border"
+ )}
+ style={{
+ left: `${startX}px`,
+ width: `${width}px`,
+ }}
+ title={
+ segment.cached
+ ? "Cached (fast playback)"
+ : "Not cached (will render)"
+ }
+ />
+ );
+ })}
+ </div>
+ );
+}