"use client";
import {
ArrowRightDoubleIcon,
MagicWand05Icon,
} from "@hugeicons/core-free-icons";
import { HugeiconsIcon } from "@hugeicons/react";
import { PanelView } from "./base-panel";
import { Button } from "@/components/ui/button";
import { useEditor } from "@/editor/use-editor";
import { useElementSelection } from "@/timeline/hooks/element/use-element-selection";
import { isVisualElement } from "@/timeline/element-utils";
import type { TimelineElement, TimelineTrack } from "@/timeline";
import {
type TransitionKind,
withVisualTransitionParams,
} from "@/rendering/transition";
const transitions: Array<{
kind: TransitionKind;
name: string;
description: string;
inDuration: number;
outDuration: number;
}> = [
{
kind: "fade",
name: "Soft fade",
description: "Clean dissolve for slides and stills.",
inDuration: 0.35,
outDuration: 0,
},
{
kind: "push-left",
name: "Slide left",
description: "Deck-like horizontal slide motion.",
inDuration: 0.45,
outDuration: 0,
},
{
kind: "push-right",
name: "Slide right",
description: "Reverse horizontal slide motion.",
inDuration: 0.45,
outDuration: 0,
},
{
kind: "zoom",
name: "Zoom in",
description: "Subtle punch-in with a fade edge.",
inDuration: 0.4,
outDuration: 0,
},
{
kind: "zoom-out",
name: "Zoom out",
description: "Settle inward from a wider opening.",
inDuration: 0.4,
outDuration: 0,
},
{
kind: "spin",
name: "Spin dissolve",
description: "A restrained turn with a soft fade.",
inDuration: 0.45,
outDuration: 0,
},
];
export function TransitionsView() {
const editor = useEditor();
const { selectedElements } = useElementSelection();
const scene = useEditor((currentEditor) =>
currentEditor.scenes.getActiveSceneOrNull(),
);
const currentTime = useEditor((currentEditor) =>
currentEditor.playback.getCurrentTime(),
);
const selectedVisualElements = editor.timeline
.getElementsWithTracks({ elements: selectedElements })
.filter(({ element }) => isVisualElement(element));
const nearestTarget = scene
? findNearestTransitionTarget({
tracks: [scene.tracks.main, ...scene.tracks.overlay],
currentTime,
})
: null;
const targets =
selectedVisualElements.length > 0
? selectedVisualElements
: nearestTarget
? [nearestTarget]
: [];
const selectedCount = selectedVisualElements.length;
const applyTransition = ({
kind,
inDuration,
outDuration,
}: {
kind: TransitionKind;
inDuration: number;
outDuration: number;
}) => {
if (targets.length === 0) {
return;
}
editor.timeline.updateElements({
updates: targets.map(({ track, element }) => ({
trackId: track.id,
elementId: element.id,
patch: {
params: withVisualTransitionParams({
params: element.params,
transitionKind: kind,
inDuration,
outDuration,
}),
},
})),
});
};
return (
{selectedCount > 0
? `${selectedCount} selected clip${selectedCount === 1 ? "" : "s"}.`
: nearestTarget
? `Ready for the cut into ${nearestTarget.element.name}.`
: "Add at least two image or video clips to one track."}