refactor: extract timeline model types
Move project entity shapes into the model layer so sibling domains no longer depend on timeline for core data types. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
79cc7d2159
commit
6ac2116daa
|
|
@ -1,6 +1,6 @@
|
|||
import { getKeyframeById } from "@/animation";
|
||||
import type { SelectedKeyframeRef } from "@/animation/types";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import { elementId, type ElementRef, type TimelineElement } from "@/model";
|
||||
import { PasteKeyframesCommand } from "@/commands/timeline";
|
||||
import type {
|
||||
ClipboardHandler,
|
||||
|
|
@ -12,7 +12,7 @@ function resolveSingleSourceElement({
|
|||
selectedKeyframes,
|
||||
}: {
|
||||
selectedKeyframes: SelectedKeyframeRef[];
|
||||
}) {
|
||||
}): ElementRef | null {
|
||||
const firstKeyframe = selectedKeyframes[0];
|
||||
if (!firstKeyframe) {
|
||||
return null;
|
||||
|
|
@ -20,7 +20,7 @@ function resolveSingleSourceElement({
|
|||
|
||||
const sourceElement = {
|
||||
trackId: firstKeyframe.trackId,
|
||||
elementId: firstKeyframe.elementId,
|
||||
elementId: elementId(firstKeyframe.elementId),
|
||||
};
|
||||
const isSingleSource = selectedKeyframes.every(
|
||||
(keyframe) =>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,7 @@ import type {
|
|||
SelectedKeyframeRef,
|
||||
} from "@/animation/types";
|
||||
import type { Command } from "@/commands/base-command";
|
||||
import type {
|
||||
CreateTimelineElement,
|
||||
ElementRef,
|
||||
TrackType,
|
||||
} from "@/timeline";
|
||||
import type { CreateTimelineElement, ElementRef, TrackType } from "@/model";
|
||||
|
||||
export interface ElementClipboardItem {
|
||||
trackId: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { EditorSelectionPatch } from "@/selection/editor-selection";
|
||||
import type { ElementRef } from "@/timeline/types";
|
||||
import type { ElementRef } from "@/model";
|
||||
|
||||
export interface CommandResult {
|
||||
selection?: EditorSelectionPatch;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { storageService } from "@/services/storage/service";
|
|||
import { videoCache } from "@/services/video-cache/service";
|
||||
import { waveformCache } from "@/services/waveform-cache/service";
|
||||
import { hasMediaId } from "@/timeline/element-utils";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class RemoveMediaAssetCommand extends Command {
|
||||
private savedAssets: MediaAsset[] | null = null;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TScene } from "@/timeline";
|
||||
import type { TScene } from "@/model";
|
||||
import { buildDefaultScene } from "@/timeline/scenes";
|
||||
|
||||
export class CreateSceneCommand extends Command {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TScene } from "@/timeline";
|
||||
import type { TScene } from "@/model";
|
||||
import { canDeleteScene, getFallbackSceneAfterDelete } from "@/timeline/scenes";
|
||||
|
||||
export class DeleteSceneCommand extends Command {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TScene } from "@/timeline";
|
||||
import type { TScene } from "@/model";
|
||||
import { updateSceneInArray } from "@/timeline/scenes";
|
||||
import { getFrameTime, moveBookmarkInArray } from "@/timeline/bookmarks/index";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TScene } from "@/timeline";
|
||||
import type { TScene } from "@/model";
|
||||
import { updateSceneInArray } from "@/timeline/scenes";
|
||||
import {
|
||||
getFrameTime,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TScene } from "@/timeline";
|
||||
import type { TScene } from "@/model";
|
||||
import { updateSceneInArray } from "@/timeline/scenes";
|
||||
|
||||
export class RenameSceneCommand extends Command {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TScene } from "@/timeline";
|
||||
import type { TScene } from "@/model";
|
||||
import { updateSceneInArray } from "@/timeline/scenes";
|
||||
import {
|
||||
getFrameTime,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { Bookmark, TScene } from "@/timeline";
|
||||
import type { Bookmark, TScene } from "@/model";
|
||||
import { updateSceneInArray } from "@/timeline/scenes";
|
||||
import {
|
||||
getFrameTime,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from "@/animation";
|
||||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { KeyframeClipboardItem } from "@/clipboard";
|
||||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import type { SceneTracks, TimelineElement } from "@/model";
|
||||
import { updateElementInSceneTracks } from "@/timeline";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
type CommandResult,
|
||||
} from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import type { ElementRef, SceneTracks, TimelineElement } from "@/model";
|
||||
import type { ElementClipboardItem } from "@/clipboard";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import {
|
||||
|
|
@ -16,7 +16,7 @@ import { cloneAnimations } from "@/animation";
|
|||
|
||||
export class PasteCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
private pastedElements: { trackId: string; elementId: string }[] = [];
|
||||
private pastedElements: ElementRef[] = [];
|
||||
private readonly time: number;
|
||||
private readonly clipboardItems: ElementClipboardItem[];
|
||||
|
||||
|
|
@ -142,7 +142,7 @@ export class PasteCommand extends Command {
|
|||
}
|
||||
}
|
||||
|
||||
getPastedElements(): { trackId: string; elementId: string }[] {
|
||||
getPastedElements(): ElementRef[] {
|
||||
return this.pastedElements;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { TimelineTrack } from "@/timeline";
|
||||
import type { TimelineTrack } from "@/model";
|
||||
|
||||
function removeTrackElements<TTrack extends TimelineTrack>({
|
||||
track,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,13 @@ import {
|
|||
createElementSelectionResult,
|
||||
type CommandResult,
|
||||
} from "@/commands/base-command";
|
||||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import {
|
||||
elementId,
|
||||
type ElementId,
|
||||
type ElementRef,
|
||||
type SceneTracks,
|
||||
type TimelineElement,
|
||||
} from "@/model";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import { EditorCore } from "@/core";
|
||||
import { applyPlacement, resolveTrackPlacement } from "@/timeline/placement";
|
||||
|
|
@ -14,7 +20,7 @@ interface DuplicateElementsParams {
|
|||
}
|
||||
|
||||
export class DuplicateElementsCommand extends Command {
|
||||
private duplicatedElements: { trackId: string; elementId: string }[] = [];
|
||||
private duplicatedElements: ElementRef[] = [];
|
||||
private savedState: SceneTracks | null = null;
|
||||
private elements: DuplicateElementsParams["elements"];
|
||||
|
||||
|
|
@ -53,7 +59,7 @@ export class DuplicateElementsCommand extends Command {
|
|||
continue;
|
||||
}
|
||||
|
||||
const newId = generateUUID();
|
||||
const newId = elementId(generateUUID());
|
||||
newTrackElements.push(
|
||||
buildDuplicateElement({
|
||||
element,
|
||||
|
|
@ -107,7 +113,7 @@ export class DuplicateElementsCommand extends Command {
|
|||
}
|
||||
}
|
||||
|
||||
getDuplicatedElements(): { trackId: string; elementId: string }[] {
|
||||
getDuplicatedElements(): ElementRef[] {
|
||||
return this.duplicatedElements;
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +124,7 @@ function buildDuplicateElement({
|
|||
startTime,
|
||||
}: {
|
||||
element: TimelineElement;
|
||||
id: string;
|
||||
id: ElementId;
|
||||
startTime: number;
|
||||
}): TimelineElement {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import { isVisualElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/model";
|
||||
import { buildDefaultEffectInstance } from "@/effects";
|
||||
|
||||
function addEffectToElement({
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import { isVisualElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/model";
|
||||
|
||||
function removeEffectFromElement({
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import { isVisualElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/model";
|
||||
|
||||
function reorderEffectsOnElement({
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import { isVisualElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/model";
|
||||
|
||||
export function toggleEffectOnElement({
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { Command, type CommandResult } from "@/commands/base-command";
|
|||
import { EditorCore } from "@/core";
|
||||
import { isVisualElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type { SceneTracks, VisualElement } from "@/timeline";
|
||||
import type { SceneTracks, VisualElement } from "@/model";
|
||||
|
||||
function updateEffectParamsOnElement({
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type {
|
||||
CreateTimelineElement,
|
||||
SceneTracks,
|
||||
TimelineElement,
|
||||
TrackType,
|
||||
} from "@/timeline";
|
||||
import {
|
||||
elementId,
|
||||
type CreateTimelineElement,
|
||||
type ElementId,
|
||||
type SceneTracks,
|
||||
type TimelineElement,
|
||||
type TrackType,
|
||||
} from "@/model";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import { requiresMediaId } from "@/timeline/element-utils";
|
||||
import type { MediaAsset } from "@/media/types";
|
||||
|
|
@ -29,13 +31,13 @@ export interface InsertElementParams {
|
|||
}
|
||||
|
||||
export class InsertElementCommand extends Command {
|
||||
private elementId: string;
|
||||
private elementId: ElementId;
|
||||
private savedState: SceneTracks | null = null;
|
||||
private targetTrackId: string | null = null;
|
||||
|
||||
constructor({ element, placement }: InsertElementParams) {
|
||||
super();
|
||||
this.elementId = generateUUID();
|
||||
this.elementId = elementId(generateUUID());
|
||||
this.element = element;
|
||||
this.placement = placement;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Command, type CommandResult } from "@/commands/base-command";
|
|||
import { removeEffectParamKeyframe } from "@/animation/effect-param-channel";
|
||||
import { updateElementInSceneTracks } from "@/timeline";
|
||||
import { isVisualElement } from "@/timeline/element-utils";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class RemoveEffectParamKeyframeCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { updateElementInSceneTracks } from "@/timeline";
|
||||
import type { AnimationPath, AnimationValue } from "@/animation/types";
|
||||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import type { SceneTracks, TimelineElement } from "@/model";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
|
||||
function removeKeyframeAndPersist({
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { retimeElementKeyframe } from "@/animation";
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { updateElementInSceneTracks } from "@/timeline";
|
||||
import type { AnimationPath } from "@/animation/types";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
|
||||
export class RetimeKeyframeCommand extends Command {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type {
|
|||
AnimationPath,
|
||||
ScalarCurveKeyframePatch,
|
||||
} from "@/animation/types";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class UpdateScalarKeyframeCurveCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { updateElementInSceneTracks } from "@/timeline";
|
|||
import { isVisualElement } from "@/timeline/element-utils";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
import type { AnimationInterpolation } from "@/animation/types";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class UpsertEffectParamKeyframeCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { EditorCore } from "@/core";
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { upsertPathKeyframe } from "@/animation";
|
||||
import { updateElementInSceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
import type {
|
||||
AnimationPath,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from "@/masks/custom-path";
|
||||
import type { CustomMask } from "@/masks/types";
|
||||
import { isMaskableElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/model";
|
||||
|
||||
function deletePointsFromCustomMask({
|
||||
mask,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { insertPointOnCustomMaskSegment } from "@/masks/definitions/custom";
|
|||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type { CustomMask } from "@/masks/types";
|
||||
import { isMaskableElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/model";
|
||||
|
||||
function insertPointIntoCustomMask({
|
||||
mask,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { EditorCore } from "@/core";
|
||||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { isMaskableElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { SceneTracks, MaskableElement } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/model";
|
||||
|
||||
function removeMaskFromElement({
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { EditorCore } from "@/core";
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { isMaskableElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { Mask } from "@/masks/types";
|
||||
import type { SceneTracks, MaskableElement } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/model";
|
||||
|
||||
export function toggleMaskInvertedOnElement({
|
||||
element,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@ import {
|
|||
type CommandResult,
|
||||
} from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { SceneTracks, TimelineElement, TimelineTrack } from "@/timeline";
|
||||
import {
|
||||
elementId as toElementId,
|
||||
type SceneTracks,
|
||||
type TimelineElement,
|
||||
type TimelineTrack,
|
||||
} from "@/model";
|
||||
import {
|
||||
buildEmptyTrack,
|
||||
validateElementTrackCompatibility,
|
||||
|
|
@ -117,7 +122,7 @@ export class MoveElementCommand extends Command {
|
|||
return createElementSelectionResult(
|
||||
this.moves.map(({ elementId, targetTrackId }) => ({
|
||||
trackId: targetTrackId,
|
||||
elementId,
|
||||
elementId: toElementId(elementId),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,12 @@ import {
|
|||
createElementSelectionResult,
|
||||
type CommandResult,
|
||||
} from "@/commands/base-command";
|
||||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import {
|
||||
elementId,
|
||||
type ElementRef,
|
||||
type SceneTracks,
|
||||
type TimelineElement,
|
||||
} from "@/model";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import { EditorCore } from "@/core";
|
||||
import { isRetimableElement } from "@/timeline";
|
||||
|
|
@ -12,7 +17,7 @@ import { getSourceSpanAtClipTime } from "@/retime";
|
|||
|
||||
export class SplitElementsCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
private rightSideElements: { trackId: string; elementId: string }[] = [];
|
||||
private rightSideElements: ElementRef[] = [];
|
||||
private readonly elements: { trackId: string; elementId: string }[];
|
||||
private readonly splitTime: number;
|
||||
private readonly retainSide: "both" | "left" | "right";
|
||||
|
|
@ -32,7 +37,7 @@ export class SplitElementsCommand extends Command {
|
|||
this.retainSide = retainSide;
|
||||
}
|
||||
|
||||
getRightSideElements(): { trackId: string; elementId: string }[] {
|
||||
getRightSideElements(): ElementRef[] {
|
||||
return this.rightSideElements;
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +112,7 @@ export class SplitElementsCommand extends Command {
|
|||
},
|
||||
];
|
||||
} else if (this.retainSide === "right") {
|
||||
const newId = generateUUID();
|
||||
const newId = elementId(generateUUID());
|
||||
this.rightSideElements.push({
|
||||
trackId: track.id,
|
||||
elementId: newId,
|
||||
|
|
@ -126,7 +131,7 @@ export class SplitElementsCommand extends Command {
|
|||
];
|
||||
} else {
|
||||
// "both" - split into two pieces
|
||||
const secondElementId = generateUUID();
|
||||
const secondElementId = elementId(generateUUID());
|
||||
this.rightSideElements.push({
|
||||
trackId: track.id,
|
||||
elementId: secondElementId,
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ import {
|
|||
resolveTrackPlacement,
|
||||
} from "@/timeline/placement";
|
||||
import { updateElementInSceneTracks } from "@/timeline/track-element-update";
|
||||
import type {
|
||||
SceneTracks,
|
||||
TimelineElement,
|
||||
VideoElement,
|
||||
} from "@/timeline/types";
|
||||
import {
|
||||
elementId,
|
||||
type SceneTracks,
|
||||
type TimelineElement,
|
||||
type VideoElement,
|
||||
} from "@/model";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
|
||||
export class ToggleSourceAudioSeparationCommand extends Command {
|
||||
|
|
@ -75,7 +76,7 @@ export class ToggleSourceAudioSeparationCommand extends Command {
|
|||
...buildSeparatedAudioElement({
|
||||
sourceElement: videoElement,
|
||||
}),
|
||||
id: generateUUID(),
|
||||
id: elementId(generateUUID()),
|
||||
};
|
||||
const placementResult = resolveTrackPlacement({
|
||||
tracks: this.savedState,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { EditorCore } from "@/core";
|
||||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import type { SceneTracks, TimelineElement } from "@/model";
|
||||
import {
|
||||
findTrackInSceneTracks,
|
||||
updateElementInSceneTracks,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { SceneTracks, TrackType } from "@/timeline";
|
||||
import type { SceneTracks, TrackType } from "@/model";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import { EditorCore } from "@/core";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { EditorCore } from "@/core";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class RemoveTrackCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { EditorCore } from "@/core";
|
||||
import { canTrackHaveAudio, findTrackInSceneTracks, updateTrackInSceneTracks } from "@/timeline";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { EditorCore } from "@/core";
|
||||
import { canTrackBeHidden, findTrackInSceneTracks, updateTrackInSceneTracks } from "@/timeline";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { EditorCore } from "@/core";
|
||||
|
||||
export class TracksSnapshotCommand extends Command {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import {
|
|||
type MediaViewMode,
|
||||
useAssetsPanelStore,
|
||||
} from "@/components/editor/panels/assets/assets-panel-store";
|
||||
import { MASKABLE_ELEMENT_TYPES } from "@/timeline";
|
||||
import { MASKABLE_ELEMENT_TYPES } from "@/model";
|
||||
import type { MediaAsset } from "@/media/types";
|
||||
import { cn } from "@/utils/ui";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
hasKeyframesForPath,
|
||||
} from "@/animation";
|
||||
import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import type { TimelineElement } from "@/model";
|
||||
import { upsertElementKeyframe } from "@/timeline/animation-properties";
|
||||
|
||||
export function useKeyframedColorProperty({
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
hasKeyframesForPath,
|
||||
} from "@/animation";
|
||||
import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import type { TimelineElement } from "@/model";
|
||||
import { upsertElementKeyframe } from "@/timeline/animation-properties";
|
||||
import { snapToStep } from "@/utils/math";
|
||||
import { usePropertyDraft } from "./use-property-draft";
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import {
|
|||
getAnimationParamValueKind,
|
||||
} from "@/animation/animated-params";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import type { TimelineElement } from "@/model";
|
||||
|
||||
export interface KeyframedParamPropertyResult {
|
||||
hasAnimatedKeyframes: boolean;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,5 @@
|
|||
import type { ReactNode } from "react";
|
||||
import type {
|
||||
EffectElement,
|
||||
GraphicElement,
|
||||
ImageElement,
|
||||
MaskableElement,
|
||||
RetimableElement,
|
||||
StickerElement,
|
||||
TextElement,
|
||||
VisualElement,
|
||||
VideoElement,
|
||||
AudioElement,
|
||||
TimelineElement,
|
||||
} from "@/timeline";
|
||||
import type { AudioElement, EffectElement, GraphicElement, ImageElement, MaskableElement, RetimableElement, StickerElement, TextElement, TimelineElement, VideoElement, VisualElement } from "@/model";
|
||||
import type { MediaAsset } from "@/media/types";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { EditorCore } from "@/core";
|
|||
import type { Command, CommandResult } from "@/commands";
|
||||
import type { EditorSelectionSnapshot } from "@/selection/editor-selection";
|
||||
import { applyRippleAdjustments, computeRippleAdjustments } from "@/ripple";
|
||||
import type { SceneTracks } from "@/timeline/types";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
interface CommandHistoryEntry {
|
||||
command: Command;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { EditorCore } from "@/core";
|
||||
import type { SceneTracks, TScene } from "@/timeline";
|
||||
import type { SceneTracks, TScene } from "@/model";
|
||||
import { storageService } from "@/services/storage/service";
|
||||
import {
|
||||
getMainScene,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type {
|
|||
EditorSelectionSnapshot,
|
||||
SelectedMaskPointSelection,
|
||||
} from "@/selection/editor-selection";
|
||||
import type { ElementRef } from "@/timeline/types";
|
||||
import type { ElementRef } from "@/model";
|
||||
|
||||
export class SelectionManager {
|
||||
private selectedElements: ElementRef[] = [];
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
import type { EditorCore } from "@/core";
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type {
|
||||
SceneTracks,
|
||||
TrackType,
|
||||
TimelineTrack,
|
||||
TimelineElement,
|
||||
RetimeConfig,
|
||||
} from "@/timeline";
|
||||
import type { RetimeConfig, SceneTracks, TimelineElement, TimelineTrack, TrackType } from "@/model";
|
||||
import { calculateTotalDuration } from "@/timeline";
|
||||
import { TimelineDragSource } from "@/timeline/drag-source";
|
||||
import { findTrackInSceneTracks } from "@/timeline/track-element-update";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useState } from "react";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type { Effect } from "@/effects/types";
|
||||
import type { EffectElement, VisualElement } from "@/timeline";
|
||||
import type { EffectElement, VisualElement } from "@/model";
|
||||
import { effectsRegistry } from "@/effects";
|
||||
import { useEditor } from "@/editor/use-editor";
|
||||
import { useElementPreview } from "@/timeline/hooks/use-element-preview";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { buildDefaultParamValues } from "@/params/registry";
|
|||
import { effectsRegistry } from "./registry";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type { Effect, EffectDefinition, EffectPass } from "@/effects/types";
|
||||
import { VISUAL_ELEMENT_TYPES } from "@/timeline";
|
||||
import { VISUAL_ELEMENT_TYPES } from "@/model";
|
||||
|
||||
export { effectsRegistry } from "./registry";
|
||||
export { registerDefaultEffects } from "./definitions";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
type KeyframedParamPropertyResult,
|
||||
} from "@/components/editor/panels/properties/hooks/use-keyframed-param-property";
|
||||
import type { ParamDefinition, ParamValues } from "@/params";
|
||||
import type { GraphicElement } from "@/timeline";
|
||||
import type { GraphicElement } from "@/model";
|
||||
import {
|
||||
graphicsRegistry,
|
||||
registerDefaultGraphics,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import type { MaskableElement } from "@/timeline";
|
||||
import type { MaskableElement } from "@/model";
|
||||
import type { Mask, MaskType, TextMask } from "@/masks/types";
|
||||
import type { NumberParamDefinition, SelectParamDefinition } from "@/params";
|
||||
import { masksRegistry, buildDefaultMaskInstance } from "@/masks";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
import { MIN_MASK_DIMENSION } from "@/masks/dimensions";
|
||||
import type { RectangleMaskParams } from "@/masks/types";
|
||||
|
||||
|
|
@ -100,10 +100,10 @@ export function toGlobalMaskSnapLines({
|
|||
bounds,
|
||||
canvasSize,
|
||||
}: {
|
||||
lines: SnapLine[];
|
||||
lines: PreviewSnapLine[];
|
||||
bounds: ElementBounds;
|
||||
canvasSize: { width: number; height: number };
|
||||
}): SnapLine[] {
|
||||
}): PreviewSnapLine[] {
|
||||
const centerX = bounds.cx - canvasSize.width / 2;
|
||||
const centerY = bounds.cy - canvasSize.height / 2;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
snapScale,
|
||||
snapScaleAxes,
|
||||
type ScaleEdgePreference,
|
||||
type SnapLine,
|
||||
type PreviewSnapLine,
|
||||
} from "@/preview/preview-snap";
|
||||
import type { RectangleMaskParams, SplitMaskParams } from "@/masks/types";
|
||||
import {
|
||||
|
|
@ -20,7 +20,7 @@ type SharedMaskParams = SplitMaskParams | RectangleMaskParams;
|
|||
|
||||
type MaskSnapResult<TParams extends SharedMaskParams> = {
|
||||
params: TParams;
|
||||
activeLines: SnapLine[];
|
||||
activeLines: PreviewSnapLine[];
|
||||
};
|
||||
|
||||
const CORNER_SIZE_HANDLES = new Set([
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { CustomMaskPathPoint } from "@/masks/custom-path";
|
||||
import type {
|
||||
|
|
@ -261,7 +261,7 @@ export interface MaskSnapResult<
|
|||
TParams extends BaseMaskParams = BaseMaskParams,
|
||||
> {
|
||||
params: TParams;
|
||||
activeLines: SnapLine[];
|
||||
activeLines: PreviewSnapLine[];
|
||||
}
|
||||
|
||||
export interface MaskInteractionResult {
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import {
|
|||
} from "@/preview/element-bounds";
|
||||
import {
|
||||
SNAP_THRESHOLD_SCREEN_PIXELS,
|
||||
type SnapLine,
|
||||
type PreviewSnapLine,
|
||||
} from "@/preview/preview-snap";
|
||||
import type { SelectedMaskPointSelection } from "@/selection/editor-selection";
|
||||
import type { Mask, MaskInteractionResult } from "@/masks/types";
|
||||
import type { MaskableElement } from "@/timeline";
|
||||
import type { MaskableElement } from "@/model";
|
||||
import { registerCanceller } from "@/editor/cancel-interaction";
|
||||
|
||||
interface DragState {
|
||||
|
|
@ -94,7 +94,7 @@ function withUpdatedMaskParams<TMask extends Mask>({
|
|||
export function useMaskHandles({
|
||||
onSnapLinesChange,
|
||||
}: {
|
||||
onSnapLinesChange?: (lines: SnapLine[]) => void;
|
||||
onSnapLinesChange?: (lines: PreviewSnapLine[]) => void;
|
||||
}) {
|
||||
const editor = useEditor();
|
||||
const isShiftHeldRef = useShiftKey();
|
||||
|
|
@ -552,7 +552,7 @@ export function useMaskHandles({
|
|||
screenPixels: SNAP_THRESHOLD_SCREEN_PIXELS,
|
||||
});
|
||||
const { params: nextParams, activeLines } = isShiftHeldRef.current
|
||||
? { params: proposedParams, activeLines: [] as SnapLine[] }
|
||||
? { params: proposedParams, activeLines: [] as PreviewSnapLine[] }
|
||||
: (def.interaction.snap?.({
|
||||
handleId: drag.handleId,
|
||||
startParams: drag.startParams,
|
||||
|
|
@ -560,7 +560,7 @@ export function useMaskHandles({
|
|||
bounds: selectedWithMask.bounds,
|
||||
canvasSize,
|
||||
snapThreshold,
|
||||
}) ?? { params: proposedParams, activeLines: [] as SnapLine[] });
|
||||
}) ?? { params: proposedParams, activeLines: [] as PreviewSnapLine[] });
|
||||
|
||||
onSnapLinesChange?.(activeLines);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,4 @@
|
|||
import type {
|
||||
AudioElement,
|
||||
VideoElement,
|
||||
LibraryAudioElement,
|
||||
RetimeConfig,
|
||||
SceneTracks,
|
||||
} from "@/timeline";
|
||||
import type { AudioElement, LibraryAudioElement, RetimeConfig, SceneTracks, VideoElement } from "@/model";
|
||||
import { shouldMaintainPitch } from "@/retime/rate";
|
||||
import type { MediaAsset } from "@/media/types";
|
||||
import { applyAudioMasteringToBuffer } from "@/media/audio-mastering";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
type VideoCodec,
|
||||
} from "mediabunny";
|
||||
import { createTimelineAudioBuffer } from "@/media/audio";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import type { MediaAsset } from "@/media/types";
|
||||
import { TICKS_PER_SECOND } from "@/wasm";
|
||||
import { renderThumbnailDataUrl } from "./thumbnail";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { getSourceTimeAtClipTime } from "@/retime";
|
||||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
|
||||
const RMS_ANALYSIS_WINDOW_SECONDS = 0.02;
|
||||
const DEFAULT_SOURCE_WAVEFORM_BUCKET_SIZE = 128;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
export interface Bookmark {
|
||||
time: number;
|
||||
note?: string;
|
||||
color?: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import type { ElementId } from "./ids";
|
||||
|
||||
export type ElementRef = {
|
||||
trackId: string;
|
||||
elementId: ElementId;
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import type { BaseTimelineElement, RetimeConfig } from "./base";
|
||||
|
||||
export interface BaseAudioElement extends BaseTimelineElement {
|
||||
type: "audio";
|
||||
volume: number;
|
||||
muted?: boolean;
|
||||
buffer?: AudioBuffer;
|
||||
retime?: RetimeConfig;
|
||||
}
|
||||
|
||||
export interface UploadAudioElement extends BaseAudioElement {
|
||||
sourceType: "upload";
|
||||
mediaId: string;
|
||||
}
|
||||
|
||||
export interface LibraryAudioElement extends BaseAudioElement {
|
||||
sourceType: "library";
|
||||
sourceUrl: string;
|
||||
}
|
||||
|
||||
export type AudioElement = UploadAudioElement | LibraryAudioElement;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import type { ElementAnimations } from "@/animation/types";
|
||||
import type { ElementId } from "../ids";
|
||||
|
||||
export interface BaseTimelineElement {
|
||||
id: ElementId;
|
||||
name: string;
|
||||
duration: number;
|
||||
startTime: number;
|
||||
trimStart: number;
|
||||
trimEnd: number;
|
||||
sourceDuration?: number;
|
||||
animations?: ElementAnimations;
|
||||
}
|
||||
|
||||
export interface RetimeConfig {
|
||||
rate: number;
|
||||
maintainPitch?: boolean;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import type { ParamValues } from "@/params";
|
||||
import type { BaseTimelineElement } from "./base";
|
||||
|
||||
export interface EffectElement extends BaseTimelineElement {
|
||||
type: "effect";
|
||||
effectType: string;
|
||||
params: ParamValues;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import type { Effect } from "@/effects/types";
|
||||
import type { Mask } from "@/masks/types";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type { BlendMode, Transform } from "@/rendering";
|
||||
import type { BaseTimelineElement } from "./base";
|
||||
|
||||
export interface GraphicElement extends BaseTimelineElement {
|
||||
type: "graphic";
|
||||
definitionId: string;
|
||||
params: ParamValues;
|
||||
hidden?: boolean;
|
||||
transform: Transform;
|
||||
opacity: number;
|
||||
blendMode?: BlendMode;
|
||||
effects?: Effect[];
|
||||
masks?: Mask[];
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import type { Effect } from "@/effects/types";
|
||||
import type { Mask } from "@/masks/types";
|
||||
import type { BlendMode, Transform } from "@/rendering";
|
||||
import type { BaseTimelineElement } from "./base";
|
||||
|
||||
export interface ImageElement extends BaseTimelineElement {
|
||||
type: "image";
|
||||
mediaId: string;
|
||||
hidden?: boolean;
|
||||
transform: Transform;
|
||||
opacity: number;
|
||||
blendMode?: BlendMode;
|
||||
effects?: Effect[];
|
||||
masks?: Mask[];
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import type { AudioElement, LibraryAudioElement, UploadAudioElement } from "./audio";
|
||||
import type { EffectElement } from "./effect";
|
||||
import type { GraphicElement } from "./graphic";
|
||||
import type { ImageElement } from "./image";
|
||||
import type { StickerElement } from "./sticker";
|
||||
import type { TextElement } from "./text";
|
||||
import type { VideoElement } from "./video";
|
||||
import type { Transform } from "@/rendering";
|
||||
|
||||
export * from "./audio";
|
||||
export * from "./base";
|
||||
export * from "./effect";
|
||||
export * from "./graphic";
|
||||
export * from "./image";
|
||||
export * from "./sticker";
|
||||
export * from "./text";
|
||||
export * from "./video";
|
||||
|
||||
export type ElementUpdatePatch =
|
||||
| { transform: Transform }
|
||||
| { opacity: number }
|
||||
| { volume: number };
|
||||
|
||||
export type TimelineElement =
|
||||
| AudioElement
|
||||
| VideoElement
|
||||
| ImageElement
|
||||
| TextElement
|
||||
| StickerElement
|
||||
| GraphicElement
|
||||
| EffectElement;
|
||||
|
||||
export type ElementType = TimelineElement["type"];
|
||||
|
||||
function elementTypes<T extends ElementType[]>(...types: T): T {
|
||||
return types;
|
||||
}
|
||||
|
||||
export const MASKABLE_ELEMENT_TYPES = elementTypes("video", "image", "graphic");
|
||||
|
||||
export type MaskableElement = Extract<
|
||||
TimelineElement,
|
||||
{ type: (typeof MASKABLE_ELEMENT_TYPES)[number] }
|
||||
>;
|
||||
|
||||
export const RETIMABLE_ELEMENT_TYPES = elementTypes("video", "audio");
|
||||
|
||||
export type RetimableElement = Extract<
|
||||
TimelineElement,
|
||||
{ type: (typeof RETIMABLE_ELEMENT_TYPES)[number] }
|
||||
>;
|
||||
|
||||
export const VISUAL_ELEMENT_TYPES = elementTypes(
|
||||
"video",
|
||||
"image",
|
||||
"text",
|
||||
"sticker",
|
||||
"graphic",
|
||||
);
|
||||
|
||||
export type VisualElement = Extract<
|
||||
TimelineElement,
|
||||
{ type: (typeof VISUAL_ELEMENT_TYPES)[number] }
|
||||
>;
|
||||
|
||||
export type CreateUploadAudioElement = Omit<UploadAudioElement, "id">;
|
||||
export type CreateLibraryAudioElement = Omit<LibraryAudioElement, "id">;
|
||||
export type CreateAudioElement =
|
||||
| CreateUploadAudioElement
|
||||
| CreateLibraryAudioElement;
|
||||
export type CreateVideoElement = Omit<VideoElement, "id">;
|
||||
export type CreateImageElement = Omit<ImageElement, "id">;
|
||||
export type CreateTextElement = Omit<TextElement, "id">;
|
||||
export type CreateStickerElement = Omit<StickerElement, "id">;
|
||||
export type CreateGraphicElement = Omit<GraphicElement, "id">;
|
||||
export type CreateEffectElement = Omit<EffectElement, "id">;
|
||||
export type CreateTimelineElement =
|
||||
| CreateAudioElement
|
||||
| CreateVideoElement
|
||||
| CreateImageElement
|
||||
| CreateTextElement
|
||||
| CreateStickerElement
|
||||
| CreateGraphicElement
|
||||
| CreateEffectElement;
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import type { Effect } from "@/effects/types";
|
||||
import type { BlendMode, Transform } from "@/rendering";
|
||||
import type { BaseTimelineElement } from "./base";
|
||||
|
||||
export interface StickerElement extends BaseTimelineElement {
|
||||
type: "sticker";
|
||||
stickerId: string;
|
||||
/** Natural dimensions of the sticker asset, stored at insert time. Used by renderer and preview bounds to avoid split-brain geometry. */
|
||||
intrinsicWidth?: number;
|
||||
intrinsicHeight?: number;
|
||||
hidden?: boolean;
|
||||
transform: Transform;
|
||||
opacity: number;
|
||||
blendMode?: BlendMode;
|
||||
effects?: Effect[];
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import type { Effect } from "@/effects/types";
|
||||
import type { BlendMode, Transform } from "@/rendering";
|
||||
import type { BaseTimelineElement } from "./base";
|
||||
|
||||
export interface TextBackground {
|
||||
enabled: boolean;
|
||||
color: string;
|
||||
cornerRadius?: number;
|
||||
paddingX?: number;
|
||||
paddingY?: number;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
}
|
||||
|
||||
export interface TextElement extends BaseTimelineElement {
|
||||
type: "text";
|
||||
content: string;
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
color: string;
|
||||
background: TextBackground;
|
||||
textAlign: "left" | "center" | "right";
|
||||
fontWeight: "normal" | "bold";
|
||||
fontStyle: "normal" | "italic";
|
||||
textDecoration: "none" | "underline" | "line-through";
|
||||
letterSpacing?: number;
|
||||
lineHeight?: number;
|
||||
hidden?: boolean;
|
||||
transform: Transform;
|
||||
opacity: number;
|
||||
blendMode?: BlendMode;
|
||||
effects?: Effect[];
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import type { Effect } from "@/effects/types";
|
||||
import type { Mask } from "@/masks/types";
|
||||
import type { BlendMode, Transform } from "@/rendering";
|
||||
import type { BaseTimelineElement, RetimeConfig } from "./base";
|
||||
|
||||
export interface VideoElement extends BaseTimelineElement {
|
||||
type: "video";
|
||||
mediaId: string;
|
||||
volume?: number;
|
||||
muted?: boolean;
|
||||
isSourceAudioEnabled?: boolean;
|
||||
hidden?: boolean;
|
||||
retime?: RetimeConfig;
|
||||
transform: Transform;
|
||||
opacity: number;
|
||||
blendMode?: BlendMode;
|
||||
effects?: Effect[];
|
||||
masks?: Mask[];
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
export type ElementId = string & { readonly __brand: "ElementId" };
|
||||
export type TrackId = string & { readonly __brand: "TrackId" };
|
||||
export type SceneId = string & { readonly __brand: "SceneId" };
|
||||
|
||||
export function elementId(value: string): ElementId {
|
||||
return value as ElementId;
|
||||
}
|
||||
|
||||
export function trackId(value: string): TrackId {
|
||||
return value as TrackId;
|
||||
}
|
||||
|
||||
export function sceneId(value: string): SceneId {
|
||||
return value as SceneId;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export * from "./bookmark";
|
||||
export * from "./element-ref";
|
||||
export * from "./elements";
|
||||
export * from "./ids";
|
||||
export * from "./scene";
|
||||
export * from "./time";
|
||||
export * from "./track";
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import type { Bookmark } from "./bookmark";
|
||||
import type { SceneTracks } from "./track";
|
||||
|
||||
export interface TScene {
|
||||
id: string;
|
||||
name: string;
|
||||
isMain: boolean;
|
||||
tracks: SceneTracks;
|
||||
bookmarks: Bookmark[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export type MediaTime = number & { readonly __brand: "MediaTime" };
|
||||
|
||||
export function mediaTime(value: number): MediaTime {
|
||||
return value as MediaTime;
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import type {
|
||||
AudioElement,
|
||||
EffectElement,
|
||||
GraphicElement,
|
||||
ImageElement,
|
||||
StickerElement,
|
||||
TextElement,
|
||||
VideoElement,
|
||||
} from "./elements";
|
||||
|
||||
export type TrackType = "video" | "text" | "audio" | "graphic" | "effect";
|
||||
|
||||
export interface BaseTrack {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface VideoTrack extends BaseTrack {
|
||||
type: "video";
|
||||
elements: (VideoElement | ImageElement)[];
|
||||
muted: boolean;
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export interface TextTrack extends BaseTrack {
|
||||
type: "text";
|
||||
elements: TextElement[];
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export interface AudioTrack extends BaseTrack {
|
||||
type: "audio";
|
||||
elements: AudioElement[];
|
||||
muted: boolean;
|
||||
}
|
||||
|
||||
export interface GraphicTrack extends BaseTrack {
|
||||
type: "graphic";
|
||||
elements: (StickerElement | GraphicElement)[];
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export interface EffectTrack extends BaseTrack {
|
||||
type: "effect";
|
||||
elements: EffectElement[];
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
export type TimelineTrack =
|
||||
| VideoTrack
|
||||
| TextTrack
|
||||
| AudioTrack
|
||||
| GraphicTrack
|
||||
| EffectTrack;
|
||||
|
||||
export type OverlayTrack = VideoTrack | TextTrack | GraphicTrack | EffectTrack;
|
||||
|
||||
export interface SceneTracks {
|
||||
overlay: OverlayTrack[];
|
||||
main: VideoTrack;
|
||||
audio: AudioTrack[];
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
import { PEN_CURSOR } from "@/preview/components/cursors";
|
||||
import { usePreviewViewport } from "@/preview/components/preview-viewport";
|
||||
import { useMaskHandles } from "@/masks/use-mask-handles";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
import {
|
||||
CornerHandle,
|
||||
CircleHandle,
|
||||
|
|
@ -22,7 +22,7 @@ import { Rotate01Icon, FeatherIcon } from "@hugeicons/core-free-icons";
|
|||
export function MaskHandles({
|
||||
onSnapLinesChange,
|
||||
}: {
|
||||
onSnapLinesChange?: (lines: SnapLine[]) => void;
|
||||
onSnapLinesChange?: (lines: PreviewSnapLine[]) => void;
|
||||
}) {
|
||||
const viewport = usePreviewViewport();
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState } from "react";
|
||||
import { usePreviewViewport } from "@/preview/components/preview-viewport";
|
||||
import { usePreviewInteraction } from "@/preview/hooks/use-preview-interaction";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
import { TransformHandles } from "./transform-handles";
|
||||
import { MaskHandles } from "./mask-handles";
|
||||
import { SnapGuides } from "./snap-guides";
|
||||
|
|
@ -10,7 +10,7 @@ import { usePropertiesStore } from "@/components/editor/panels/properties/stores
|
|||
import { useEditor } from "@/editor/use-editor";
|
||||
|
||||
export function PreviewInteractionOverlay() {
|
||||
const [snapLines, setSnapLines] = useState<SnapLine[]>([]);
|
||||
const [snapLines, setSnapLines] = useState<PreviewSnapLine[]>([]);
|
||||
const editor = useEditor();
|
||||
const viewport = usePreviewViewport();
|
||||
const selectedElements = useEditor((e) => e.selection.getSelectedElements());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import { usePreviewViewport } from "@/preview/components/preview-viewport";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
|
||||
export function SnapGuides({ lines }: { lines: SnapLine[] }) {
|
||||
export function SnapGuides({ lines }: { lines: PreviewSnapLine[] }) {
|
||||
const viewport = usePreviewViewport();
|
||||
|
||||
if (lines.length === 0) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { usePreviewViewport } from "@/preview/components/preview-viewport";
|
||||
import { useEditor } from "@/editor/use-editor";
|
||||
import type { TextElement } from "@/timeline";
|
||||
import type { TextElement } from "@/model";
|
||||
import { DEFAULTS } from "@/timeline/defaults";
|
||||
import {
|
||||
getElementLocalTime,
|
||||
|
|
|
|||
|
|
@ -15,19 +15,12 @@ import {
|
|||
import {
|
||||
SNAP_THRESHOLD_SCREEN_PIXELS,
|
||||
snapPosition,
|
||||
type SnapLine,
|
||||
type PreviewSnapLine,
|
||||
} from "@/preview/preview-snap";
|
||||
import type { TCanvasSize } from "@/project/types";
|
||||
import type { Transform } from "@/rendering";
|
||||
import { isVisualElement } from "@/timeline/element-utils";
|
||||
import type {
|
||||
ElementRef,
|
||||
SceneTracks,
|
||||
TextElement,
|
||||
TimelineElement,
|
||||
TimelineTrack,
|
||||
VisualElement,
|
||||
} from "@/timeline";
|
||||
import type { ElementRef, SceneTracks, TextElement, TimelineElement, TimelineTrack, VisualElement } from "@/model";
|
||||
|
||||
const MIN_DRAG_DISTANCE = 0.5;
|
||||
const PRIMARY_POINTER_BUTTON = 0;
|
||||
|
|
@ -133,7 +126,7 @@ export interface PlaybackApi {
|
|||
|
||||
export interface PreviewOptions {
|
||||
isMaskMode: () => boolean;
|
||||
onSnapLinesChange?: (lines: SnapLine[]) => void;
|
||||
onSnapLinesChange?: (lines: PreviewSnapLine[]) => void;
|
||||
}
|
||||
|
||||
export interface PreviewInteractionDeps {
|
||||
|
|
@ -554,7 +547,7 @@ export class PreviewInteractionController {
|
|||
})
|
||||
: {
|
||||
snappedPosition: proposedPosition,
|
||||
activeLines: [] as SnapLine[],
|
||||
activeLines: [] as PreviewSnapLine[],
|
||||
};
|
||||
|
||||
this.deps.preview.onSnapLinesChange?.(activeLines);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
snapScale,
|
||||
snapScaleAxes,
|
||||
type ScaleEdgePreference,
|
||||
type SnapLine,
|
||||
type PreviewSnapLine,
|
||||
} from "@/preview/preview-snap";
|
||||
import { isVisualElement } from "@/timeline/element-utils";
|
||||
import {
|
||||
|
|
@ -25,12 +25,7 @@ import {
|
|||
import type { ElementAnimations } from "@/animation/types";
|
||||
import type { Transform } from "@/rendering";
|
||||
import { resolveTransformAtTime } from "@/rendering/animation-values";
|
||||
import type {
|
||||
ElementRef,
|
||||
SceneTracks,
|
||||
TimelineElement,
|
||||
VisualElement,
|
||||
} from "@/timeline";
|
||||
import type { ElementRef, SceneTracks, TimelineElement, VisualElement } from "@/model";
|
||||
|
||||
type Point = { readonly x: number; readonly y: number };
|
||||
type CanvasSize = { readonly width: number; readonly height: number };
|
||||
|
|
@ -137,7 +132,7 @@ export interface TimelineOps {
|
|||
}
|
||||
|
||||
export interface PreviewOptions {
|
||||
onSnapLinesChange?: (lines: SnapLine[]) => void;
|
||||
onSnapLinesChange?: (lines: PreviewSnapLine[]) => void;
|
||||
}
|
||||
|
||||
export interface TransformHandleDeps {
|
||||
|
|
@ -590,7 +585,7 @@ export class TransformHandleController {
|
|||
screenPixels: SNAP_THRESHOLD_SCREEN_PIXELS,
|
||||
});
|
||||
const { snappedScale, activeLines } = this.deps.input.isShiftHeld()
|
||||
? { snappedScale: scaleFactor, activeLines: [] as SnapLine[] }
|
||||
? { snappedScale: scaleFactor, activeLines: [] as PreviewSnapLine[] }
|
||||
: snapScale({
|
||||
proposedScale: scaleFactor,
|
||||
position: session.initialTransform.position,
|
||||
|
|
@ -670,12 +665,12 @@ export class TransformHandleController {
|
|||
x: {
|
||||
snappedScale: proposedScaleX,
|
||||
snapDistance: Infinity,
|
||||
activeLines: [] as SnapLine[],
|
||||
activeLines: [] as PreviewSnapLine[],
|
||||
},
|
||||
y: {
|
||||
snappedScale: proposedScaleY,
|
||||
snapDistance: Infinity,
|
||||
activeLines: [] as SnapLine[],
|
||||
activeLines: [] as PreviewSnapLine[],
|
||||
},
|
||||
}
|
||||
: snapScaleAxes({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { SceneTracks, TimelineElement } from "@/timeline";
|
||||
import type { ElementId, SceneTracks, TimelineElement } from "@/model";
|
||||
import type { MediaAsset } from "@/media/types";
|
||||
import { STICKER_INTRINSIC_SIZE_FALLBACK } from "@/stickers/intrinsic-size";
|
||||
import { DEFAULT_GRAPHIC_SOURCE_SIZE } from "@/graphics";
|
||||
|
|
@ -18,7 +18,7 @@ export interface ElementBounds {
|
|||
|
||||
export interface ElementWithBounds {
|
||||
trackId: string;
|
||||
elementId: string;
|
||||
elementId: ElementId;
|
||||
element: TimelineElement;
|
||||
bounds: ElementBounds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ElementWithBounds } from "./element-bounds";
|
||||
import type { ElementRef } from "@/timeline/types";
|
||||
import type { ElementRef } from "@/model";
|
||||
|
||||
function pointInRotatedRect({
|
||||
px,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useEffect, useReducer, useRef } from "react";
|
|||
import { useEditor } from "@/editor/use-editor";
|
||||
import { useShiftKey } from "@/hooks/use-shift-key";
|
||||
import { usePreviewViewport } from "@/preview/components/preview-viewport";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
import { registerCanceller } from "@/editor/cancel-interaction";
|
||||
import {
|
||||
PreviewInteractionController,
|
||||
|
|
@ -10,7 +10,7 @@ import {
|
|||
type PreviewInteractionDepsRef,
|
||||
} from "@/preview/controllers/preview-interaction-controller";
|
||||
|
||||
export type OnSnapLinesChange = (lines: SnapLine[]) => void;
|
||||
export type OnSnapLinesChange = (lines: PreviewSnapLine[]) => void;
|
||||
|
||||
export function usePreviewInteraction({
|
||||
onSnapLinesChange,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export interface SnapLine {
|
||||
export interface PreviewSnapLine {
|
||||
type: "horizontal" | "vertical";
|
||||
position: number;
|
||||
}
|
||||
|
|
@ -8,9 +8,9 @@ const ROTATION_SNAP_THRESHOLD_DEGREES = 5;
|
|||
export const MIN_SCALE = 0.01;
|
||||
export const SNAP_THRESHOLD_SCREEN_PIXELS = 8;
|
||||
|
||||
export interface SnapResult {
|
||||
export interface PreviewSnapResult {
|
||||
snappedPosition: { x: number; y: number };
|
||||
activeLines: SnapLine[];
|
||||
activeLines: PreviewSnapLine[];
|
||||
}
|
||||
|
||||
type ScaleEdge = "left" | "right" | "top" | "bottom";
|
||||
|
|
@ -78,7 +78,7 @@ export function snapPosition({
|
|||
elementSize: { width: number; height: number };
|
||||
rotation?: number;
|
||||
snapThreshold: { x: number; y: number };
|
||||
}): SnapResult {
|
||||
}): PreviewSnapResult {
|
||||
const centerX = 0;
|
||||
const centerY = 0;
|
||||
const left = -canvasSize.width / 2;
|
||||
|
|
@ -91,11 +91,11 @@ export function snapPosition({
|
|||
const sinR = Math.abs(Math.sin(rotRad));
|
||||
const halfWidth = (elementSize.width * cosR + elementSize.height * sinR) / 2;
|
||||
const halfHeight = (elementSize.width * sinR + elementSize.height * cosR) / 2;
|
||||
const activeLines: SnapLine[] = [];
|
||||
const activeLines: PreviewSnapLine[] = [];
|
||||
|
||||
type AxisSnapCandidate = {
|
||||
snappedPosition: number;
|
||||
line: SnapLine;
|
||||
line: PreviewSnapLine;
|
||||
distance: number;
|
||||
};
|
||||
|
||||
|
|
@ -183,7 +183,7 @@ export function snapPosition({
|
|||
|
||||
export interface ScaleSnapResult {
|
||||
snappedScale: number;
|
||||
activeLines: SnapLine[];
|
||||
activeLines: PreviewSnapLine[];
|
||||
}
|
||||
|
||||
export function snapScale({
|
||||
|
|
@ -226,7 +226,7 @@ export function snapScale({
|
|||
interface SnapCandidate {
|
||||
scale: number;
|
||||
distance: number;
|
||||
lines: SnapLine[];
|
||||
lines: PreviewSnapLine[];
|
||||
edge: ScaleEdge;
|
||||
}
|
||||
|
||||
|
|
@ -320,10 +320,10 @@ export function snapScale({
|
|||
const snappedTop = position.y - aabbBaseHalfH * best.scale;
|
||||
const snappedBottom = position.y + aabbBaseHalfH * best.scale;
|
||||
|
||||
const activeLines: SnapLine[] = [];
|
||||
const activeLines: PreviewSnapLine[] = [];
|
||||
const seenKeys = new Set<string>();
|
||||
|
||||
function addLine({ line }: { line: SnapLine }) {
|
||||
function addLine({ line }: { line: PreviewSnapLine }) {
|
||||
const key = `${line.type}-${line.position}`;
|
||||
if (!seenKeys.has(key)) {
|
||||
seenKeys.add(key);
|
||||
|
|
@ -368,7 +368,7 @@ export interface AxisSnapResult {
|
|||
snappedScale: number;
|
||||
/** Infinity when no snap candidate was within threshold */
|
||||
snapDistance: number;
|
||||
activeLines: SnapLine[];
|
||||
activeLines: PreviewSnapLine[];
|
||||
}
|
||||
|
||||
export function snapScaleAxes({
|
||||
|
|
@ -413,7 +413,7 @@ export function snapScaleAxes({
|
|||
interface Candidate {
|
||||
scale: number;
|
||||
distance: number;
|
||||
line: SnapLine;
|
||||
line: PreviewSnapLine;
|
||||
edge: ScaleEdge;
|
||||
}
|
||||
|
||||
|
|
@ -442,7 +442,7 @@ export function snapScaleAxes({
|
|||
|
||||
if (cosR > EPSILON) {
|
||||
for (const T of [canvasLeft, 0, canvasRight]) {
|
||||
const line: SnapLine = { type: "vertical", position: T };
|
||||
const line: PreviewSnapLine = { type: "vertical", position: T };
|
||||
const distLeft = Math.abs(currentLeftEdge - T);
|
||||
if (distLeft <= snapThreshold.x) {
|
||||
const scale = (2 * (position.x - T) - yContribW) / (baseWidth * cosR);
|
||||
|
|
@ -458,7 +458,7 @@ export function snapScaleAxes({
|
|||
|
||||
if (sinR > EPSILON) {
|
||||
for (const T of [canvasTop, 0, canvasBottom]) {
|
||||
const line: SnapLine = { type: "horizontal", position: T };
|
||||
const line: PreviewSnapLine = { type: "horizontal", position: T };
|
||||
const distTop = Math.abs(currentTopEdge - T);
|
||||
if (distTop <= snapThreshold.y) {
|
||||
const scale = (2 * (position.y - T) - yContribH) / (baseWidth * sinR);
|
||||
|
|
@ -480,7 +480,7 @@ export function snapScaleAxes({
|
|||
|
||||
if (sinR > EPSILON) {
|
||||
for (const T of [canvasLeft, 0, canvasRight]) {
|
||||
const line: SnapLine = { type: "vertical", position: T };
|
||||
const line: PreviewSnapLine = { type: "vertical", position: T };
|
||||
const distLeft = Math.abs(currentLeftEdge - T);
|
||||
if (distLeft <= snapThreshold.x) {
|
||||
const scale = (2 * (position.x - T) - xContribW) / (baseHeight * sinR);
|
||||
|
|
@ -496,7 +496,7 @@ export function snapScaleAxes({
|
|||
|
||||
if (cosR > EPSILON) {
|
||||
for (const T of [canvasTop, 0, canvasBottom]) {
|
||||
const line: SnapLine = { type: "horizontal", position: T };
|
||||
const line: PreviewSnapLine = { type: "horizontal", position: T };
|
||||
const distTop = Math.abs(currentTopEdge - T);
|
||||
if (distTop <= snapThreshold.y) {
|
||||
const scale = (2 * (position.y - T) - xContribH) / (baseHeight * cosR);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { FrameRate } from "opencut-wasm";
|
||||
import type { TScene } from "@/timeline/types";
|
||||
import type { TScene } from "@/model";
|
||||
|
||||
export type TBackground =
|
||||
| {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import type { BlendMode } from "@/rendering";
|
||||
import type { ElementType } from "@/timeline";
|
||||
import type { ElementType } from "@/model";
|
||||
import type { ElementAnimations } from "@/animation/types";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
import { RainDropIcon } from "@hugeicons/core-free-icons";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { NumberField } from "@/components/ui/number-field";
|
||||
import { useEditor } from "@/editor/use-editor";
|
||||
import { clamp, isNearlyEqual } from "@/utils/math";
|
||||
import type { VisualElement } from "@/timeline";
|
||||
import type { VisualElement } from "@/model";
|
||||
import {
|
||||
Section,
|
||||
SectionContent,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
getSourceTimeAtClipTime,
|
||||
getTimelineDurationForSourceSpan,
|
||||
} from "@/retime";
|
||||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
|
||||
const twoX: RetimeConfig = { rate: 2 };
|
||||
const halfX: RetimeConfig = { rate: 0.5 };
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
getSourceSpanAtClipTime,
|
||||
splitRetimeAtClipTime,
|
||||
} from "@/retime";
|
||||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
|
||||
describe("retime split", () => {
|
||||
test("measures source span at a clip time", () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { PitchShifter } from "soundtouchjs";
|
||||
import { clampRetimeRate, shouldMaintainPitch } from "@/retime/rate";
|
||||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
import { getSourceTimeAtClipTime } from "./resolve";
|
||||
|
||||
const RATE_EPSILON = 1e-6;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
import { clampRetimeRate } from "@/retime/rate";
|
||||
|
||||
export function buildConstantRetime({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
import { clampRetimeRate } from "@/retime/rate";
|
||||
|
||||
function getSafeRate({ rate }: { rate: number }): number {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
import { getSourceTimeAtClipTime } from "./resolve";
|
||||
|
||||
export function getSourceSpanAtClipTime({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { SceneTracks, TimelineTrack } from "@/timeline/types";
|
||||
import type { SceneTracks, TimelineTrack } from "@/model";
|
||||
import { rippleShiftElements } from "./shift";
|
||||
|
||||
export interface RippleAdjustment {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { SceneTracks, TimelineElement, TimelineTrack } from "@/timeline/types";
|
||||
import type { SceneTracks, TimelineElement, TimelineTrack } from "@/model";
|
||||
import type { RippleAdjustment } from "./apply";
|
||||
|
||||
interface Interval {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { TimelineElement } from "@/timeline/types";
|
||||
import type { TimelineElement } from "@/model";
|
||||
|
||||
export function rippleShiftElements<TElement extends TimelineElement>({
|
||||
elements,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { SelectedKeyframeRef } from "@/animation/types";
|
||||
import type { ElementRef } from "@/timeline/types";
|
||||
import type { ElementRef } from "@/model";
|
||||
|
||||
export interface SelectedMaskPointSelection {
|
||||
trackId: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { EffectPass } from "@/effects/types";
|
||||
import type { RetimeConfig } from "@/timeline";
|
||||
import type { RetimeConfig } from "@/model";
|
||||
import { BaseNode } from "./base-node";
|
||||
|
||||
export type BlurBackgroundNodeParams = {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue