"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimelineSequenceRightEdgeDragHandle = exports.useTimelineSequenceFromDrag = exports.TimelineSequenceLeftEdgeDragHandle = exports.getTimelineSequenceFromDragTargets = exports.getTimelineSequenceLeftEdgeDragTargets = exports.getTimelineSequenceDurationDragTargets = exports.getTimelineSequenceFromDragKeyframeMoves = exports.getTimelineSequenceFromDragChanges = exports.getTimelineSequenceFromDragValue = exports.getTimelineSequenceDurationDragChanges = exports.getTimelineSequenceLeftEdgeDragChanges = exports.getTimelineSequenceLeftEdgeDragValues = exports.getTimelineSequenceLeftEdgeDragDelta = exports.getTimelineSequenceDurationDragValue = exports.isTimelineSequenceDurationDraggable = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const studio_shared_1 = require("@remotion/studio-shared");
const react_1 = require("react");
const remotion_1 = require("remotion");
const no_react_1 = require("remotion/no-react");
const calculate_timeline_1 = require("../../helpers/calculate-timeline");
const client_id_1 = require("../../helpers/client-id");
const colors_1 = require("../../helpers/colors");
const timeline_layout_1 = require("../../helpers/timeline-layout");
const ForceSpecificCursor_1 = require("../ForceSpecificCursor");
const save_sequence_prop_1 = require("./save-sequence-prop");
const TimelineSelection_1 = require("./TimelineSelection");
const HANDLE_WIDTH = 6;
const baseStyle = {
    position: 'absolute',
    top: 0,
    bottom: 0,
    width: HANDLE_WIDTH,
    cursor: 'ew-resize',
    zIndex: 1,
    touchAction: 'none',
};
const getKeyframedSequenceDragTargets = ({ nodePath, sequence, propStatuses, }) => {
    var _a;
    const status = propStatuses[remotion_1.Internals.makeSequencePropsSubscriptionKey(nodePath)];
    if (status === null || status === undefined || !status.canUpdate) {
        return { effectKeyframes: [], sequenceKeyframes: [] };
    }
    const sequenceSchema = (_a = sequence.controls) === null || _a === void 0 ? void 0 : _a.schema;
    const sequenceKeyframes = sequenceSchema === undefined
        ? []
        : Object.entries(status.props).flatMap(([fieldKey, propStatus]) => propStatus.status === 'keyframed'
            ? [{ fieldKey, schema: sequenceSchema, status: propStatus }]
            : []);
    const effectKeyframes = status.effects.flatMap((effectStatus) => {
        var _a;
        if (!effectStatus.canUpdate) {
            return [];
        }
        const effectSchema = (_a = sequence.effects[effectStatus.effectIndex]) === null || _a === void 0 ? void 0 : _a.schema;
        if (effectSchema === undefined) {
            return [];
        }
        return Object.entries(effectStatus.props).flatMap(([fieldKey, propStatus]) => propStatus.status === 'keyframed'
            ? [
                {
                    effectIndex: effectStatus.effectIndex,
                    fieldKey,
                    schema: effectSchema,
                    status: propStatus,
                },
            ]
            : []);
    });
    return { effectKeyframes, sequenceKeyframes };
};
const shiftKeyframedStatus = ({ status, deltaFrames, }) => {
    return {
        ...status,
        keyframes: status.keyframes.map((keyframe) => ({
            ...keyframe,
            frame: keyframe.frame + deltaFrames,
        })),
    };
};
const canUpdateDurationInFrames = ({ propStatuses, nodePath, }) => {
    var _a, _b;
    const status = (_b = (_a = remotion_1.Internals.getPropStatusesCtx(propStatuses, nodePath)) === null || _a === void 0 ? void 0 : _a.durationInFrames) === null || _b === void 0 ? void 0 : _b.status;
    return status === 'static';
};
const canUpdateFrom = ({ propStatuses, nodePath, }) => {
    var _a, _b;
    const status = (_b = (_a = remotion_1.Internals.getPropStatusesCtx(propStatuses, nodePath)) === null || _a === void 0 ? void 0 : _a.from) === null || _b === void 0 ? void 0 : _b.status;
    return status === 'static';
};
const canUpdateTrimBefore = ({ propStatuses, nodePath, }) => {
    var _a, _b;
    const status = (_b = (_a = remotion_1.Internals.getPropStatusesCtx(propStatuses, nodePath)) === null || _a === void 0 ? void 0 : _a.trimBefore) === null || _b === void 0 ? void 0 : _b.status;
    return status === 'static';
};
const isTimelineSequenceDurationDraggable = (sequence) => {
    var _a;
    const isInteractiveSeriesSequence = ((_a = sequence.controls) === null || _a === void 0 ? void 0 : _a.componentIdentity) ===
        'dev.remotion.remotion.Series.Sequence';
    return ((sequence.type === 'sequence' ||
        sequence.type === 'image' ||
        sequence.type === 'audio' ||
        sequence.type === 'video') &&
        !sequence.loopDisplay &&
        (!sequence.isInsideSeries || isInteractiveSeriesSequence) &&
        Boolean(sequence.controls));
};
exports.isTimelineSequenceDurationDraggable = isTimelineSequenceDurationDraggable;
const isLeftEdgeDraggableSequence = (sequence) => {
    return (!sequence.isInsideSeries &&
        Boolean(sequence.controls) &&
        (sequence.type === 'sequence' ||
            sequence.type === 'image' ||
            sequence.type === 'audio' ||
            sequence.type === 'video'));
};
const isFromDraggableSequence = (sequence) => {
    return (!sequence.loopDisplay &&
        !sequence.isInsideSeries &&
        Boolean(sequence.controls));
};
const getTimelineSequenceDurationDragValue = ({ initialDuration, deltaFrames, }) => Math.max(1, initialDuration + deltaFrames);
exports.getTimelineSequenceDurationDragValue = getTimelineSequenceDurationDragValue;
const getTimelineSequenceLeftEdgeDragDelta = ({ initialDuration, initialTrimBefore, deltaFrames, }) => {
    const minDeltaFrames = 0 - initialTrimBefore;
    const maxDeltaFrames = initialDuration - 1;
    return Math.max(minDeltaFrames, Math.min(deltaFrames, maxDeltaFrames));
};
exports.getTimelineSequenceLeftEdgeDragDelta = getTimelineSequenceLeftEdgeDragDelta;
const getTimelineSequenceLeftEdgeDragValues = ({ initialDuration, initialFrom, initialTrimBefore, deltaFrames, }) => {
    const clampedDeltaFrames = (0, exports.getTimelineSequenceLeftEdgeDragDelta)({
        initialDuration,
        initialTrimBefore,
        deltaFrames,
    });
    return {
        durationInFrames: initialDuration - clampedDeltaFrames,
        from: initialFrom + clampedDeltaFrames,
        trimBefore: initialTrimBefore + clampedDeltaFrames,
    };
};
exports.getTimelineSequenceLeftEdgeDragValues = getTimelineSequenceLeftEdgeDragValues;
const getTimelineSequenceLeftEdgeDragChanges = ({ targets, deltaFrames, }) => {
    return targets.flatMap((target) => {
        const nextValues = (0, exports.getTimelineSequenceLeftEdgeDragValues)({
            initialDuration: target.initialDuration,
            initialFrom: target.initialFrom,
            initialTrimBefore: target.initialTrimBefore,
            deltaFrames,
        });
        const changes = [];
        if (nextValues.from !== target.initialFrom) {
            changes.push({
                fileName: target.fileName,
                nodePath: target.nodePath,
                fieldKey: 'from',
                value: nextValues.from,
                defaultValue: '0',
                schema: target.schema,
            });
        }
        if (nextValues.durationInFrames !== target.initialDuration) {
            changes.push({
                fileName: target.fileName,
                nodePath: target.nodePath,
                fieldKey: 'durationInFrames',
                value: nextValues.durationInFrames,
                defaultValue: null,
                schema: target.schema,
            });
        }
        if (nextValues.trimBefore !== target.initialTrimBefore) {
            changes.push({
                fileName: target.fileName,
                nodePath: target.nodePath,
                fieldKey: 'trimBefore',
                value: nextValues.trimBefore,
                defaultValue: '0',
                schema: target.schema,
            });
        }
        return changes;
    });
};
exports.getTimelineSequenceLeftEdgeDragChanges = getTimelineSequenceLeftEdgeDragChanges;
const getTimelineSequenceDurationDragChanges = ({ targets, deltaFrames, }) => {
    return targets.flatMap((target) => {
        const nextValue = (0, exports.getTimelineSequenceDurationDragValue)({
            initialDuration: target.initialDuration,
            deltaFrames,
        });
        if (nextValue === target.initialDuration) {
            return [];
        }
        return [
            {
                fileName: target.fileName,
                nodePath: target.nodePath,
                fieldKey: 'durationInFrames',
                value: nextValue,
                defaultValue: null,
                schema: target.schema,
            },
        ];
    });
};
exports.getTimelineSequenceDurationDragChanges = getTimelineSequenceDurationDragChanges;
const getTimelineSequenceFromDragValue = ({ initialFrom, deltaFrames, }) => initialFrom + deltaFrames;
exports.getTimelineSequenceFromDragValue = getTimelineSequenceFromDragValue;
const getTimelineSequenceFromDragChanges = ({ targets, deltaFrames, }) => {
    return targets.flatMap((target) => {
        const nextValue = (0, exports.getTimelineSequenceFromDragValue)({
            initialFrom: target.initialFrom,
            deltaFrames,
        });
        if (nextValue === target.initialFrom) {
            return [];
        }
        return [
            {
                fileName: target.fileName,
                nodePath: target.nodePath,
                fieldKey: 'from',
                value: nextValue,
                defaultValue: '0',
                schema: no_react_1.NoReactInternals.sequenceSchema,
            },
        ];
    });
};
exports.getTimelineSequenceFromDragChanges = getTimelineSequenceFromDragChanges;
const getTimelineSequenceFromDragKeyframeMoves = ({ targets, deltaFrames, }) => {
    if (deltaFrames === 0) {
        return { effectKeyframes: [], sequenceKeyframes: [] };
    }
    return {
        sequenceKeyframes: targets.flatMap((target) => target.sequenceKeyframes.flatMap((keyframeTarget) => keyframeTarget.status.keyframes.map((keyframe) => ({
            fileName: target.fileName,
            nodePath: target.nodePath,
            fieldKey: keyframeTarget.fieldKey,
            fromFrame: keyframe.frame,
            toFrame: keyframe.frame + deltaFrames,
            schema: keyframeTarget.schema,
        })))),
        effectKeyframes: targets.flatMap((target) => target.effectKeyframes.flatMap((keyframeTarget) => keyframeTarget.status.keyframes.map((keyframe) => ({
            fileName: target.fileName,
            nodePath: target.nodePath,
            effectIndex: keyframeTarget.effectIndex,
            fieldKey: keyframeTarget.fieldKey,
            fromFrame: keyframe.frame,
            toFrame: keyframe.frame + deltaFrames,
            schema: keyframeTarget.schema,
        })))),
    };
};
exports.getTimelineSequenceFromDragKeyframeMoves = getTimelineSequenceFromDragKeyframeMoves;
const findSequenceTrack = ({ tracks, nodePathInfo, }) => {
    const key = (0, studio_shared_1.stringifySequenceExpandedRowKey)(nodePathInfo.sequenceSubscriptionKey);
    return tracks.find((candidate) => {
        if (candidate.nodePathInfo === null) {
            return false;
        }
        return ((0, studio_shared_1.stringifySequenceExpandedRowKey)(candidate.nodePathInfo.sequenceSubscriptionKey) === key && candidate.nodePathInfo.index === nodePathInfo.index);
    });
};
const getTimelineSequenceDurationDragTargets = ({ draggedNodePathInfo, selectedItems, sequences, overrideIdsToNodePaths, propStatuses, }) => {
    const draggedSelectionKey = (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(draggedNodePathInfo);
    const selectedSequenceItems = selectedItems.filter((item) => item.type === 'sequence');
    const draggedItemIsSelected = selectedSequenceItems.some((item) => (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(item.nodePathInfo) ===
        draggedSelectionKey);
    if (draggedItemIsSelected &&
        selectedSequenceItems.length !== selectedItems.length) {
        return null;
    }
    const targetNodePathInfos = draggedItemIsSelected && selectedSequenceItems.length > 1
        ? selectedSequenceItems.map((item) => item.nodePathInfo)
        : [draggedNodePathInfo];
    const tracks = (0, calculate_timeline_1.calculateTimeline)({ sequences, overrideIdsToNodePaths });
    const targets = new Map();
    for (const nodePathInfo of targetNodePathInfos) {
        const track = findSequenceTrack({ tracks, nodePathInfo });
        const originalSequence = track
            ? sequences.find((sequence) => sequence.id === track.sequence.id)
            : null;
        if (!track ||
            !track.nodePathInfo ||
            !originalSequence ||
            !(0, exports.isTimelineSequenceDurationDraggable)(originalSequence)) {
            return null;
        }
        const nodePath = track.nodePathInfo.sequenceSubscriptionKey;
        if (!canUpdateDurationInFrames({ propStatuses, nodePath })) {
            return null;
        }
        const { controls } = originalSequence;
        if (!controls) {
            return null;
        }
        const key = (0, studio_shared_1.stringifySequenceSubscriptionKey)(nodePath);
        if (!targets.has(key)) {
            targets.set(key, {
                fileName: nodePath.absolutePath,
                initialDuration: originalSequence.duration,
                nodePath,
                schema: controls.schema,
            });
        }
    }
    return [...targets.values()];
};
exports.getTimelineSequenceDurationDragTargets = getTimelineSequenceDurationDragTargets;
const getTimelineSequenceLeftEdgeDragTargets = ({ draggedNodePathInfo, selectedItems, sequences, overrideIdsToNodePaths, propStatuses, }) => {
    var _a, _b;
    const draggedSelectionKey = (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(draggedNodePathInfo);
    const selectedSequenceItems = selectedItems.filter((item) => item.type === 'sequence');
    const draggedItemIsSelected = selectedSequenceItems.some((item) => (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(item.nodePathInfo) ===
        draggedSelectionKey);
    if (draggedItemIsSelected &&
        selectedSequenceItems.length !== selectedItems.length) {
        return null;
    }
    const targetNodePathInfos = draggedItemIsSelected && selectedSequenceItems.length > 1
        ? selectedSequenceItems.map((item) => item.nodePathInfo)
        : [draggedNodePathInfo];
    const tracks = (0, calculate_timeline_1.calculateTimeline)({ sequences, overrideIdsToNodePaths });
    const targets = new Map();
    for (const nodePathInfo of targetNodePathInfos) {
        const track = findSequenceTrack({ tracks, nodePathInfo });
        const originalSequence = track
            ? sequences.find((sequence) => sequence.id === track.sequence.id)
            : null;
        if (!track ||
            !track.nodePathInfo ||
            !originalSequence ||
            !isLeftEdgeDraggableSequence(originalSequence)) {
            return null;
        }
        const nodePath = track.nodePathInfo.sequenceSubscriptionKey;
        const trimsMedia = originalSequence.type === 'audio' || originalSequence.type === 'video';
        if (!canUpdateFrom({ propStatuses, nodePath }) ||
            !canUpdateDurationInFrames({ propStatuses, nodePath }) ||
            !canUpdateTrimBefore({ propStatuses, nodePath })) {
            return null;
        }
        const { controls } = originalSequence;
        if (!controls) {
            return null;
        }
        const key = (0, studio_shared_1.stringifySequenceSubscriptionKey)(nodePath);
        if (!targets.has(key)) {
            targets.set(key, {
                fileName: nodePath.absolutePath,
                initialDuration: originalSequence.duration,
                initialFrom: originalSequence.from,
                initialTrimBefore: trimsMedia
                    ? ((_a = originalSequence.trimBefore) !== null && _a !== void 0 ? _a : Math.max(0, originalSequence.startMediaFrom))
                    : ((_b = originalSequence.trimBefore) !== null && _b !== void 0 ? _b : 0),
                nodePath,
                schema: controls.schema,
            });
        }
    }
    return [...targets.values()];
};
exports.getTimelineSequenceLeftEdgeDragTargets = getTimelineSequenceLeftEdgeDragTargets;
const getTimelineSequenceFromDragTargets = ({ draggedNodePathInfo, selectedItems, sequences, overrideIdsToNodePaths, propStatuses, }) => {
    const draggedSelectionKey = (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(draggedNodePathInfo);
    const selectedSequenceItems = selectedItems.filter((item) => item.type === 'sequence');
    const draggedItemIsSelected = selectedSequenceItems.some((item) => (0, TimelineSelection_1.getTimelineSequenceSelectionKey)(item.nodePathInfo) ===
        draggedSelectionKey);
    if (draggedItemIsSelected &&
        selectedSequenceItems.length !== selectedItems.length) {
        return null;
    }
    const targetNodePathInfos = draggedItemIsSelected && selectedSequenceItems.length > 1
        ? selectedSequenceItems.map((item) => item.nodePathInfo)
        : [draggedNodePathInfo];
    const tracks = (0, calculate_timeline_1.calculateTimeline)({ sequences, overrideIdsToNodePaths });
    const targets = new Map();
    for (const nodePathInfo of targetNodePathInfos) {
        const track = findSequenceTrack({ tracks, nodePathInfo });
        const originalSequence = track
            ? sequences.find((sequence) => sequence.id === track.sequence.id)
            : null;
        if (!track ||
            !track.nodePathInfo ||
            !originalSequence ||
            !isFromDraggableSequence(originalSequence)) {
            return null;
        }
        const nodePath = track.nodePathInfo.sequenceSubscriptionKey;
        if (!canUpdateFrom({ propStatuses, nodePath })) {
            return null;
        }
        const key = (0, studio_shared_1.stringifySequenceSubscriptionKey)(nodePath);
        if (!targets.has(key)) {
            const { effectKeyframes, sequenceKeyframes } = getKeyframedSequenceDragTargets({
                nodePath,
                sequence: originalSequence,
                propStatuses,
            });
            targets.set(key, {
                effectKeyframes,
                fileName: nodePath.absolutePath,
                initialFrom: originalSequence.from,
                nodePath,
                sequenceKeyframes,
            });
        }
    }
    return [...targets.values()];
};
exports.getTimelineSequenceFromDragTargets = getTimelineSequenceFromDragTargets;
const clearLeftEdgeDragOverrides = ({ clearDragOverrides, targets, }) => {
    for (const target of targets) {
        clearDragOverrides(target.nodePath);
    }
};
const clearDurationDragOverrides = ({ clearDragOverrides, targets, }) => {
    for (const target of targets) {
        clearDragOverrides(target.nodePath);
    }
};
const clearFromDragOverrides = ({ clearDragOverrides, clearEffectDragOverrides, targets, }) => {
    for (const target of targets) {
        clearDragOverrides(target.nodePath);
        const effectIndexes = new Set(target.effectKeyframes.map((keyframe) => keyframe.effectIndex));
        for (const effectIndex of effectIndexes) {
            clearEffectDragOverrides(target.nodePath, effectIndex);
        }
    }
};
const TimelineSequenceLeftEdgeDragHandle = ({ nodePathInfo, windowWidth, timelineDurationInFrames }) => {
    const { setPropStatuses, setDragOverrides, clearDragOverrides } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
    const propStatusesRef = (0, react_1.useContext)(remotion_1.Internals.VisualModePropStatusesRefContext);
    const sequencesRef = (0, react_1.useContext)(remotion_1.Internals.SequenceManagerRefContext);
    const { overrideIdToNodePathMappings } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsGettersContext);
    const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
    const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
    const [dragging, setDragging] = (0, react_1.useState)(false);
    const dragStateRef = (0, react_1.useRef)(null);
    const latestRef = (0, react_1.useRef)({
        nodePathInfo,
        setPropStatuses,
        setDragOverrides,
        clearDragOverrides,
        previewServerState,
        overrideIdToNodePathMappings,
    });
    latestRef.current = {
        nodePathInfo,
        setPropStatuses,
        setDragOverrides,
        clearDragOverrides,
        previewServerState,
        overrideIdToNodePathMappings,
    };
    const finishDrag = (0, react_1.useCallback)((commit) => {
        const dragState = dragStateRef.current;
        dragStateRef.current = null;
        document.body.style.userSelect = '';
        document.body.style.webkitUserSelect = '';
        (0, ForceSpecificCursor_1.stopForcingSpecificCursor)();
        setDragging(false);
        if (!dragState) {
            return;
        }
        const { setPropStatuses: latestSetPropStatuses, clearDragOverrides: latestClear, previewServerState: latestServerState, } = latestRef.current;
        const changes = (0, exports.getTimelineSequenceLeftEdgeDragChanges)({
            targets: dragState.targets,
            deltaFrames: dragState.latestDeltaFrames,
        });
        if (!commit ||
            latestServerState.type !== 'connected' ||
            changes.length === 0) {
            clearLeftEdgeDragOverrides({
                clearDragOverrides: latestClear,
                targets: dragState.targets,
            });
            return;
        }
        const savePromise = (0, save_sequence_prop_1.saveSequenceProps)({
            changes,
            setPropStatuses: latestSetPropStatuses,
            clientId: latestServerState.clientId,
            undoLabel: dragState.targets.length > 1
                ? 'Resize selected sequences'
                : 'Resize sequence',
            redoLabel: dragState.targets.length > 1
                ? 'Resize selected sequences back'
                : 'Resize sequence back',
        });
        savePromise
            .catch((err) => {
            remotion_1.Internals.Log.error({ logLevel: 'error', tag: null }, 'Could not save left edge drag', err);
        })
            .finally(() => {
            clearLeftEdgeDragOverrides({
                clearDragOverrides: latestClear,
                targets: dragState.targets,
            });
        });
    }, []);
    const onPointerDown = (0, react_1.useCallback)((e) => {
        if (e.button !== 0) {
            return;
        }
        const pxPerFrame = timelineDurationInFrames > 0
            ? (windowWidth - timeline_layout_1.TIMELINE_PADDING * 2) / timelineDurationInFrames
            : 0;
        if (pxPerFrame <= 0) {
            return;
        }
        const { nodePathInfo: latestNodePathInfo, overrideIdToNodePathMappings: latestOverrideIdsToNodePaths, } = latestRef.current;
        const { selectedItems: latestSelectedItems } = currentSelection.current;
        const targets = (0, exports.getTimelineSequenceLeftEdgeDragTargets)({
            draggedNodePathInfo: latestNodePathInfo,
            selectedItems: latestSelectedItems,
            sequences: sequencesRef.current,
            overrideIdsToNodePaths: latestOverrideIdsToNodePaths,
            propStatuses: propStatusesRef.current,
        });
        if (targets === null || targets.length === 0) {
            return;
        }
        e.stopPropagation();
        e.preventDefault();
        dragStateRef.current = {
            initialClientX: e.clientX,
            latestDeltaFrames: 0,
            pxPerFrame,
            pointerId: e.pointerId,
            targets,
        };
        document.body.style.userSelect = 'none';
        document.body.style.webkitUserSelect = 'none';
        (0, ForceSpecificCursor_1.forceSpecificCursor)('ew-resize');
        setDragging(true);
    }, [
        currentSelection,
        propStatusesRef,
        sequencesRef,
        timelineDurationInFrames,
        windowWidth,
    ]);
    (0, react_1.useEffect)(() => {
        if (!dragging) {
            return;
        }
        const onMove = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            const dx = e.clientX - dragState.initialClientX;
            const deltaFrames = Math.round(dx / dragState.pxPerFrame);
            dragState.latestDeltaFrames = deltaFrames;
            for (const target of dragState.targets) {
                const nextValues = (0, exports.getTimelineSequenceLeftEdgeDragValues)({
                    initialDuration: target.initialDuration,
                    initialFrom: target.initialFrom,
                    initialTrimBefore: target.initialTrimBefore,
                    deltaFrames,
                });
                latestRef.current.setDragOverrides(target.nodePath, 'from', remotion_1.Internals.makeStaticDragOverride(nextValues.from));
                latestRef.current.setDragOverrides(target.nodePath, 'durationInFrames', remotion_1.Internals.makeStaticDragOverride(nextValues.durationInFrames));
                latestRef.current.setDragOverrides(target.nodePath, 'trimBefore', remotion_1.Internals.makeStaticDragOverride(nextValues.trimBefore));
            }
        };
        const onUp = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            finishDrag(true);
        };
        const onCancel = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            finishDrag(false);
        };
        const onWindowBlur = () => {
            finishDrag(false);
        };
        window.addEventListener('pointermove', onMove);
        window.addEventListener('pointerup', onUp);
        window.addEventListener('pointercancel', onCancel);
        window.addEventListener('blur', onWindowBlur);
        return () => {
            window.removeEventListener('pointermove', onMove);
            window.removeEventListener('pointerup', onUp);
            window.removeEventListener('pointercancel', onCancel);
            window.removeEventListener('blur', onWindowBlur);
        };
    }, [dragging, finishDrag]);
    const style = {
        ...baseStyle,
        left: 0,
        background: colors_1.TRANSPARENT,
    };
    return (jsx_runtime_1.jsx("div", { role: "separator", "aria-orientation": "vertical", title: "Drag to trim start", style: style, onPointerDown: onPointerDown }));
};
exports.TimelineSequenceLeftEdgeDragHandle = TimelineSequenceLeftEdgeDragHandle;
const useTimelineSequenceFromDrag = ({ nodePathInfo, windowWidth, timelineDurationInFrames, }) => {
    const { setPropStatuses, setDragOverrides, clearDragOverrides, setEffectDragOverrides, clearEffectDragOverrides, } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
    const propStatusesRef = (0, react_1.useContext)(remotion_1.Internals.VisualModePropStatusesRefContext);
    const sequencesRef = (0, react_1.useContext)(remotion_1.Internals.SequenceManagerRefContext);
    const { overrideIdToNodePathMappings } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsGettersContext);
    const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
    const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
    const [dragging, setDragging] = (0, react_1.useState)(false);
    const dragStateRef = (0, react_1.useRef)(null);
    const latestRef = (0, react_1.useRef)({
        nodePathInfo,
        setPropStatuses,
        setDragOverrides,
        clearDragOverrides,
        setEffectDragOverrides,
        clearEffectDragOverrides,
        previewServerState,
        overrideIdToNodePathMappings,
    });
    latestRef.current = {
        nodePathInfo,
        setPropStatuses,
        setDragOverrides,
        clearDragOverrides,
        setEffectDragOverrides,
        clearEffectDragOverrides,
        previewServerState,
        overrideIdToNodePathMappings,
    };
    const finishDrag = (0, react_1.useCallback)((commit) => {
        const dragState = dragStateRef.current;
        dragStateRef.current = null;
        document.body.style.userSelect = '';
        document.body.style.webkitUserSelect = '';
        setDragging(false);
        if (!dragState) {
            return;
        }
        const { setPropStatuses: latestSetPropStatuses, clearDragOverrides: latestClear, clearEffectDragOverrides: latestClearEffect, previewServerState: latestServerState, } = latestRef.current;
        const changes = (0, exports.getTimelineSequenceFromDragChanges)({
            targets: dragState.targets,
            deltaFrames: dragState.latestDeltaFrames,
        });
        const keyframeMoves = (0, exports.getTimelineSequenceFromDragKeyframeMoves)({
            targets: dragState.targets,
            deltaFrames: dragState.latestDeltaFrames,
        });
        if (!commit ||
            latestServerState.type !== 'connected' ||
            (changes.length === 0 &&
                keyframeMoves.sequenceKeyframes.length === 0 &&
                keyframeMoves.effectKeyframes.length === 0)) {
            clearFromDragOverrides({
                clearDragOverrides: latestClear,
                clearEffectDragOverrides: latestClearEffect,
                targets: dragState.targets,
            });
            return;
        }
        const savePromise = (0, save_sequence_prop_1.saveSequenceProps)({
            changes,
            movedKeyframes: {
                sequenceKeyframes: keyframeMoves.sequenceKeyframes,
                effectKeyframes: keyframeMoves.effectKeyframes,
            },
            setPropStatuses: latestSetPropStatuses,
            clientId: latestServerState.clientId,
            undoLabel: dragState.targets.length > 1
                ? 'Move selected sequences'
                : 'Move sequence',
            redoLabel: dragState.targets.length > 1
                ? 'Move selected sequences back'
                : 'Move sequence back',
        });
        savePromise
            .catch((err) => {
            remotion_1.Internals.Log.error({ logLevel: 'error', tag: null }, 'Could not save from', err);
        })
            .finally(() => {
            clearFromDragOverrides({
                clearDragOverrides: latestClear,
                clearEffectDragOverrides: latestClearEffect,
                targets: dragState.targets,
            });
        });
    }, []);
    const onPointerDown = (0, react_1.useCallback)((e) => {
        if (e.button !== 0) {
            return;
        }
        const pxPerFrame = timelineDurationInFrames > 0
            ? (windowWidth - timeline_layout_1.TIMELINE_PADDING * 2) / timelineDurationInFrames
            : 0;
        if (pxPerFrame <= 0) {
            return;
        }
        const { nodePathInfo: latestNodePathInfo, overrideIdToNodePathMappings: latestOverrideIdsToNodePaths, } = latestRef.current;
        const { selectedItems: latestSelectedItems } = currentSelection.current;
        if (latestNodePathInfo === null) {
            return;
        }
        const targets = (0, exports.getTimelineSequenceFromDragTargets)({
            draggedNodePathInfo: latestNodePathInfo,
            selectedItems: latestSelectedItems,
            sequences: sequencesRef.current,
            overrideIdsToNodePaths: latestOverrideIdsToNodePaths,
            propStatuses: propStatusesRef.current,
        });
        if (targets === null || targets.length === 0) {
            return;
        }
        e.preventDefault();
        dragStateRef.current = {
            initialClientX: e.clientX,
            latestDeltaFrames: 0,
            pxPerFrame,
            pointerId: e.pointerId,
            targets,
        };
        document.body.style.userSelect = 'none';
        document.body.style.webkitUserSelect = 'none';
        setDragging(true);
    }, [
        currentSelection,
        propStatusesRef,
        sequencesRef,
        timelineDurationInFrames,
        windowWidth,
    ]);
    (0, react_1.useEffect)(() => {
        if (!dragging) {
            return;
        }
        const onMove = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            const dx = e.clientX - dragState.initialClientX;
            const deltaFrames = Math.round(dx / dragState.pxPerFrame);
            dragState.latestDeltaFrames = deltaFrames;
            for (const target of dragState.targets) {
                const nextFrom = (0, exports.getTimelineSequenceFromDragValue)({
                    initialFrom: target.initialFrom,
                    deltaFrames,
                });
                latestRef.current.setDragOverrides(target.nodePath, 'from', remotion_1.Internals.makeStaticDragOverride(nextFrom));
                for (const keyframeTarget of target.sequenceKeyframes) {
                    latestRef.current.setDragOverrides(target.nodePath, keyframeTarget.fieldKey, {
                        type: 'keyframed',
                        status: shiftKeyframedStatus({
                            status: keyframeTarget.status,
                            deltaFrames,
                        }),
                    });
                }
                for (const keyframeTarget of target.effectKeyframes) {
                    latestRef.current.setEffectDragOverrides(target.nodePath, keyframeTarget.effectIndex, keyframeTarget.fieldKey, {
                        type: 'keyframed',
                        status: shiftKeyframedStatus({
                            status: keyframeTarget.status,
                            deltaFrames,
                        }),
                    });
                }
            }
        };
        const onUp = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            finishDrag(true);
        };
        const onCancel = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            finishDrag(false);
        };
        const onWindowBlur = () => {
            finishDrag(false);
        };
        window.addEventListener('pointermove', onMove);
        window.addEventListener('pointerup', onUp);
        window.addEventListener('pointercancel', onCancel);
        window.addEventListener('blur', onWindowBlur);
        return () => {
            window.removeEventListener('pointermove', onMove);
            window.removeEventListener('pointerup', onUp);
            window.removeEventListener('pointercancel', onCancel);
            window.removeEventListener('blur', onWindowBlur);
        };
    }, [dragging, finishDrag]);
    return {
        dragging,
        onPointerDown,
    };
};
exports.useTimelineSequenceFromDrag = useTimelineSequenceFromDrag;
const TimelineSequenceRightEdgeDragHandle = ({ nodePathInfo, windowWidth, timelineDurationInFrames }) => {
    const { setPropStatuses, setDragOverrides, clearDragOverrides } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
    const propStatusesRef = (0, react_1.useContext)(remotion_1.Internals.VisualModePropStatusesRefContext);
    const sequencesRef = (0, react_1.useContext)(remotion_1.Internals.SequenceManagerRefContext);
    const { overrideIdToNodePathMappings } = (0, react_1.useContext)(remotion_1.Internals.OverrideIdsToNodePathsGettersContext);
    const { previewServerState } = (0, react_1.useContext)(client_id_1.StudioServerConnectionCtx);
    const currentSelection = (0, TimelineSelection_1.useCurrentTimelineSelectionStateAsRef)();
    const [dragging, setDragging] = (0, react_1.useState)(false);
    const dragStateRef = (0, react_1.useRef)(null);
    // Keep latest props/setters available to window listeners installed once at pointerdown.
    const latestRef = (0, react_1.useRef)({
        nodePathInfo,
        setPropStatuses,
        setDragOverrides,
        clearDragOverrides,
        previewServerState,
        overrideIdToNodePathMappings,
    });
    latestRef.current = {
        nodePathInfo,
        setPropStatuses,
        setDragOverrides,
        clearDragOverrides,
        previewServerState,
        overrideIdToNodePathMappings,
    };
    const finishDrag = (0, react_1.useCallback)((commit) => {
        const dragState = dragStateRef.current;
        dragStateRef.current = null;
        document.body.style.userSelect = '';
        document.body.style.webkitUserSelect = '';
        (0, ForceSpecificCursor_1.stopForcingSpecificCursor)();
        setDragging(false);
        if (!dragState) {
            return;
        }
        const { setPropStatuses: latestSetPropStatuses, clearDragOverrides: latestClear, previewServerState: latestServerState, } = latestRef.current;
        const changes = (0, exports.getTimelineSequenceDurationDragChanges)({
            targets: dragState.targets,
            deltaFrames: dragState.latestDeltaFrames,
        });
        if (!commit ||
            latestServerState.type !== 'connected' ||
            changes.length === 0) {
            clearDurationDragOverrides({
                clearDragOverrides: latestClear,
                targets: dragState.targets,
            });
            return;
        }
        const savePromise = (0, save_sequence_prop_1.saveSequenceProps)({
            changes,
            setPropStatuses: latestSetPropStatuses,
            clientId: latestServerState.clientId,
            undoLabel: changes.length > 1 ? 'Resize selected sequences' : 'Resize sequence',
            redoLabel: changes.length > 1
                ? 'Resize selected sequences back'
                : 'Resize sequence back',
        });
        savePromise
            .catch((err) => {
            remotion_1.Internals.Log.error({ logLevel: 'error', tag: null }, 'Could not save durationInFrames', err);
        })
            .finally(() => {
            clearDurationDragOverrides({
                clearDragOverrides: latestClear,
                targets: dragState.targets,
            });
        });
    }, []);
    const onPointerDown = (0, react_1.useCallback)((e) => {
        if (e.button !== 0) {
            return;
        }
        const pxPerFrame = timelineDurationInFrames > 0
            ? (windowWidth - timeline_layout_1.TIMELINE_PADDING * 2) / timelineDurationInFrames
            : 0;
        if (pxPerFrame <= 0) {
            return;
        }
        const { nodePathInfo: latestNodePathInfo, overrideIdToNodePathMappings: latestOverrideIdsToNodePaths, } = latestRef.current;
        const { selectedItems: latestSelectedItems } = currentSelection.current;
        const targets = (0, exports.getTimelineSequenceDurationDragTargets)({
            draggedNodePathInfo: latestNodePathInfo,
            selectedItems: latestSelectedItems,
            sequences: sequencesRef.current,
            overrideIdsToNodePaths: latestOverrideIdsToNodePaths,
            propStatuses: propStatusesRef.current,
        });
        if (targets === null || targets.length === 0) {
            return;
        }
        e.stopPropagation();
        e.preventDefault();
        dragStateRef.current = {
            initialClientX: e.clientX,
            latestDeltaFrames: 0,
            pxPerFrame,
            pointerId: e.pointerId,
            targets,
        };
        document.body.style.userSelect = 'none';
        document.body.style.webkitUserSelect = 'none';
        (0, ForceSpecificCursor_1.forceSpecificCursor)('ew-resize');
        setDragging(true);
    }, [
        currentSelection,
        propStatusesRef,
        sequencesRef,
        timelineDurationInFrames,
        windowWidth,
    ]);
    // Install global pointer listeners while dragging. They survive parent re-renders
    // and element remounts that would otherwise drop React's synthetic handlers or
    // pointer capture.
    (0, react_1.useEffect)(() => {
        if (!dragging) {
            return;
        }
        const onMove = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            const dx = e.clientX - dragState.initialClientX;
            const deltaFrames = Math.round(dx / dragState.pxPerFrame);
            dragState.latestDeltaFrames = deltaFrames;
            for (const target of dragState.targets) {
                latestRef.current.setDragOverrides(target.nodePath, 'durationInFrames', remotion_1.Internals.makeStaticDragOverride((0, exports.getTimelineSequenceDurationDragValue)({
                    initialDuration: target.initialDuration,
                    deltaFrames,
                })));
            }
        };
        const onUp = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            finishDrag(true);
        };
        const onCancel = (e) => {
            const dragState = dragStateRef.current;
            if (!dragState || e.pointerId !== dragState.pointerId) {
                return;
            }
            finishDrag(false);
        };
        // Bail if the page loses focus mid-drag.
        const onWindowBlur = () => {
            finishDrag(false);
        };
        window.addEventListener('pointermove', onMove);
        window.addEventListener('pointerup', onUp);
        window.addEventListener('pointercancel', onCancel);
        window.addEventListener('blur', onWindowBlur);
        return () => {
            window.removeEventListener('pointermove', onMove);
            window.removeEventListener('pointerup', onUp);
            window.removeEventListener('pointercancel', onCancel);
            window.removeEventListener('blur', onWindowBlur);
        };
    }, [dragging, finishDrag]);
    const style = {
        ...baseStyle,
        right: 0,
        background: colors_1.TRANSPARENT,
    };
    return (jsx_runtime_1.jsx("div", { role: "separator", "aria-orientation": "vertical", title: "Drag to change duration", style: style, onPointerDown: onPointerDown }));
};
exports.TimelineSequenceRightEdgeDragHandle = TimelineSequenceRightEdgeDragHandle;
