+
+
+ Audio Mixer
+
+
+
+
+ {onSave && (
+
+ )}
+
+
+
+
+ {channels.length === 0 ? (
+
+ No audio channels available
+
+ ) : (
+ channels.map((channel) => (
+
+ ))
+ )}
+
+
+ {channels.length > 0 && (
+
+
+ {channels.filter((c) => !c.muted).length} of {channels.length}{" "}
+ channels active
+
+
+ )}
+
+ );
+}
diff --git a/src/components/molecules/PreviewTimeline.stories.tsx b/src/components/molecules/PreviewTimeline.stories.tsx
new file mode 100644
index 0000000..2b83869
--- /dev/null
+++ b/src/components/molecules/PreviewTimeline.stories.tsx
@@ -0,0 +1,162 @@
+import type { VideoClip, WaveformData } from "@/types";
+import { action } from "@storybook/addon-actions";
+import PreviewTimeline from "./PreviewTimeline";
+
+export default {
+ title: "Molecules/PreviewTimeline",
+ component: PreviewTimeline,
+ tags: ["molecules"],
+ decorators: [
+ (story: () => React.ReactNode) => (
+
+
+
+ Preview Timeline
+
+
+ {formatMs(playheadPosition)} / {formatMs(duration)}
+
+
+
+ {/* Cutlist visualization */}
+
+
+ Selected Clips ({cutlist.length})
+
+
+ {cutlist.map((clip, index) => {
+ const left = (clip.start / duration) * 100;
+ const width = ((clip.end - clip.start) / duration) * 100;
+ return (
+
+
+ {index + 1}
+
+
+ );
+ })}
+
+ {/* Playhead indicator */}
+
+
+
+
+ {/* Waveform displays */}
+
+ {waveformData.length === 0 ? (
+
+ No waveform data available
+
+ ) : (
+ waveformData.map((waveform, index) => (
+
+
+ Channel {waveform.channelId}
+
+
+
+ ))
+ )}
+
+
+ {/* Timeline ruler */}
+
+
+ {formatMs(0)}
+ {formatMs(duration / 4)}
+ {formatMs(duration / 2)}
+ {formatMs((duration * 3) / 4)}
+ {formatMs(duration)}
+
+
+
+
+ );
+}
diff --git a/src/components/organisms/VideoPreview.stories.tsx b/src/components/organisms/VideoPreview.stories.tsx
new file mode 100644
index 0000000..fb63e02
--- /dev/null
+++ b/src/components/organisms/VideoPreview.stories.tsx
@@ -0,0 +1,205 @@
+import type {
+ AudioChannel,
+ PreviewSettings,
+ VideoClip,
+ WaveformData,
+} from "@/types";
+import { action } from "@storybook/addon-actions";
+import VideoPreview from "./VideoPreview";
+
+export default {
+ title: "Organisms/VideoPreview",
+ component: VideoPreview,
+ tags: ["organisms"],
+ decorators: [
+ (story: () => React.ReactNode) => (
+