mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d957582b | ||
|
|
ab982b888e | ||
|
|
6ac2116daa | ||
|
|
79cc7d2159 | ||
|
|
8ab3aeba7c |
@@ -1,5 +1,5 @@
|
||||
import { snapToStep } from "@/utils/math";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import type { DynamicAnimationPathValue, NumericSpec } from "./types";
|
||||
|
||||
export function getAnimationParamValueKind({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ScalarAnimationKey } from "@/animation/types";
|
||||
import type { ScalarAnimationKey } from "@/model/decorations/animations";
|
||||
|
||||
const BEZIER_SOLVE_ITERATIONS = 20;
|
||||
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
import { converter, formatHex, formatHex8, parse } from "culori";
|
||||
import type {
|
||||
AnimationBindingComponent,
|
||||
AnimationBindingOfKind,
|
||||
AnimationBindingInstance,
|
||||
AnimationBindingKind,
|
||||
ColorAnimationBinding,
|
||||
DiscreteAnimationBinding,
|
||||
NumberAnimationBinding,
|
||||
AnimationPath,
|
||||
AnimationValue,
|
||||
DiscreteValue,
|
||||
Vector2AnimationBinding,
|
||||
VectorValue,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationBindingComponent, AnimationBindingInstance, AnimationBindingKind, AnimationBindingOfKind, AnimationPath, AnimationValue, ColorAnimationBinding, DiscreteAnimationBinding, DiscreteValue, NumberAnimationBinding, Vector2AnimationBinding, VectorValue } from "@/model/decorations/animations";
|
||||
import { clamp } from "@/utils/math";
|
||||
|
||||
interface LinearRgba {
|
||||
|
||||
@@ -2,11 +2,7 @@ import {
|
||||
getDefaultLeftHandle,
|
||||
getDefaultRightHandle,
|
||||
} from "@/animation/bezier";
|
||||
import type {
|
||||
CurveHandle,
|
||||
NormalizedCubicBezier,
|
||||
ScalarAnimationKey,
|
||||
} from "@/animation/types";
|
||||
import type { CurveHandle, NormalizedCubicBezier, ScalarAnimationKey } from "@/model/decorations/animations";
|
||||
|
||||
const VALUE_EPSILON = 1e-6;
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type {
|
||||
ElementAnimations,
|
||||
EffectParamPath,
|
||||
} from "@/animation/types";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type { EffectParamPath, ElementAnimations } from "@/model/decorations/animations";
|
||||
import type { ParamValues } from "@/model/decorations/param-values";
|
||||
import { removeElementKeyframe } from "./keyframes";
|
||||
import { resolveAnimationPathValueAtTime } from "./resolve";
|
||||
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import type {
|
||||
AnimationBindingInstance,
|
||||
AnimationPath,
|
||||
ElementAnimations,
|
||||
ScalarAnimationChannel,
|
||||
ScalarGraphChannel,
|
||||
ScalarGraphKeyframeContext,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationBindingInstance, AnimationPath, ElementAnimations, ScalarAnimationChannel } from "@/model/decorations/animations";
|
||||
import type { ScalarGraphChannel, ScalarGraphKeyframeContext } from "@/animation/types";
|
||||
|
||||
export interface EditableScalarChannels {
|
||||
binding: AnimationBindingInstance;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type {
|
||||
ElementAnimations,
|
||||
GraphicParamPath,
|
||||
} from "@/animation/types";
|
||||
import type { ParamDefinition, ParamValues } from "@/params";
|
||||
import type { ElementAnimations, GraphicParamPath } from "@/model/decorations/animations";
|
||||
import type { ParamDefinition, ParamValues } from "@/model/decorations/param-values";
|
||||
import { resolveAnimationPathValueAtTime } from "./resolve";
|
||||
|
||||
export const GRAPHIC_PARAM_PATH_PREFIX = "params.";
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
import type {
|
||||
AnimationChannel,
|
||||
AnimationInterpolation,
|
||||
AnimationValue,
|
||||
DiscreteAnimationChannel,
|
||||
DiscreteValue,
|
||||
ScalarAnimationChannel,
|
||||
ScalarAnimationKey,
|
||||
ScalarSegmentType,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationChannel, AnimationInterpolation, AnimationValue, DiscreteAnimationChannel, DiscreteValue, ScalarAnimationChannel, ScalarAnimationKey, ScalarSegmentType } from "@/model/decorations/animations";
|
||||
import {
|
||||
getBezierPoint,
|
||||
getDefaultLeftHandle,
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import type {
|
||||
AnimationBindingInstance,
|
||||
AnimationChannel,
|
||||
AnimationPath,
|
||||
ElementAnimations,
|
||||
ElementKeyframe,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationBindingInstance, AnimationChannel, AnimationPath, ElementAnimations } from "@/model/decorations/animations";
|
||||
import type { ElementKeyframe } from "@/animation/types";
|
||||
import {
|
||||
type AnimationComponentValue,
|
||||
composeAnimationValue,
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
import type {
|
||||
AnimationBindingInstance,
|
||||
AnimationBindingKind,
|
||||
AnimationChannel,
|
||||
AnimationInterpolation,
|
||||
AnimationPath,
|
||||
AnimationValue,
|
||||
DiscreteAnimationChannel,
|
||||
DiscreteAnimationKey,
|
||||
ElementAnimations,
|
||||
ScalarAnimationChannel,
|
||||
ScalarAnimationKey,
|
||||
ScalarCurveKeyframePatch,
|
||||
ScalarSegmentType,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationBindingInstance, AnimationBindingKind, AnimationChannel, AnimationInterpolation, AnimationPath, AnimationValue, DiscreteAnimationChannel, DiscreteAnimationKey, ElementAnimations, ScalarAnimationChannel, ScalarAnimationKey, ScalarSegmentType } from "@/model/decorations/animations";
|
||||
import type { ScalarCurveKeyframePatch } from "@/animation/types";
|
||||
import {
|
||||
cloneAnimationBinding,
|
||||
createAnimationBinding,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { AnimationPath, AnimationPropertyPath } from "@/animation/types";
|
||||
import type { AnimationPath, AnimationPropertyPath } from "@/model/decorations/animations";
|
||||
import { ANIMATION_PROPERTY_PATHS } from "./types";
|
||||
import { isEffectParamPath } from "./effect-param-channel";
|
||||
import { isGraphicParamPath } from "./graphic-param-channel";
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import type {
|
||||
AnimationPropertyGroup,
|
||||
AnimationPropertyPath,
|
||||
ElementAnimations,
|
||||
} from "@/animation/types";
|
||||
import { ANIMATION_PROPERTY_GROUPS } from "@/animation/types";
|
||||
import type { AnimationPropertyGroup, AnimationPropertyPath, ElementAnimations } from "@/model/decorations/animations";
|
||||
import { ANIMATION_PROPERTY_GROUPS } from "@/model/decorations/animations";
|
||||
import { getKeyframeAtTime } from "./keyframe-query";
|
||||
|
||||
export interface GroupKeyframeRef {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import type {
|
||||
AnimationPath,
|
||||
AnimationValueForPath,
|
||||
ElementAnimations,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationPath, AnimationValueForPath, ElementAnimations } from "@/model/decorations/animations";
|
||||
import {
|
||||
type AnimationComponentValue,
|
||||
composeAnimationValue,
|
||||
|
||||
+57
-181
@@ -1,186 +1,62 @@
|
||||
export const ANIMATION_PROPERTY_PATHS = [
|
||||
"transform.positionX",
|
||||
"transform.positionY",
|
||||
"transform.scaleX",
|
||||
"transform.scaleY",
|
||||
"transform.rotate",
|
||||
"opacity",
|
||||
"volume",
|
||||
"color",
|
||||
"background.color",
|
||||
"background.paddingX",
|
||||
"background.paddingY",
|
||||
"background.offsetX",
|
||||
"background.offsetY",
|
||||
"background.cornerRadius",
|
||||
] as const;
|
||||
import type {
|
||||
AnimationInterpolation,
|
||||
AnimationPath,
|
||||
AnimationValue,
|
||||
CurveHandle,
|
||||
ScalarAnimationChannel,
|
||||
ScalarAnimationKey,
|
||||
ScalarSegmentType,
|
||||
TangentMode,
|
||||
} from "@/model/decorations/animations";
|
||||
|
||||
export type AnimationPropertyPath = (typeof ANIMATION_PROPERTY_PATHS)[number];
|
||||
export type GraphicParamPath = `params.${string}`;
|
||||
export type EffectParamPath = `effects.${string}.params.${string}`;
|
||||
export type AnimationPath =
|
||||
| AnimationPropertyPath
|
||||
| GraphicParamPath
|
||||
| EffectParamPath;
|
||||
export type {
|
||||
AnimationBindingByKind,
|
||||
AnimationBindingComponent,
|
||||
AnimationBindingInstance,
|
||||
AnimationBindingKind,
|
||||
AnimationBindingOfKind,
|
||||
AnimationChannel,
|
||||
AnimationColorPropertyPath,
|
||||
AnimationInterpolation,
|
||||
AnimationKeyframe,
|
||||
AnimationNumericPropertyPath,
|
||||
AnimationPath,
|
||||
AnimationPropertyGroup,
|
||||
AnimationPropertyPath,
|
||||
AnimationPropertyValueMap,
|
||||
AnimationValue,
|
||||
AnimationValueForPath,
|
||||
ChannelExtrapolationMode,
|
||||
ColorAnimationBinding,
|
||||
ContinuousKeyframeInterpolation,
|
||||
CurveHandle,
|
||||
DiscreteAnimationBinding,
|
||||
DiscreteAnimationChannel,
|
||||
DiscreteAnimationKey,
|
||||
DiscreteKeyframeInterpolation,
|
||||
DiscreteValue,
|
||||
DynamicAnimationPathValue,
|
||||
EffectParamPath,
|
||||
ElementAnimationBindingMap,
|
||||
ElementAnimationChannelMap,
|
||||
ElementAnimations,
|
||||
GraphicParamPath,
|
||||
NormalizedCubicBezier,
|
||||
NumberAnimationBinding,
|
||||
NumericSpec,
|
||||
PrimitiveAnimationChannelKind,
|
||||
ScalarAnimationChannel,
|
||||
ScalarAnimationKey,
|
||||
ScalarSegmentType,
|
||||
TangentMode,
|
||||
Vector2AnimationBinding,
|
||||
VectorValue,
|
||||
} from "@/model/decorations/animations";
|
||||
|
||||
export const ANIMATION_PROPERTY_GROUPS = {
|
||||
"transform.scale": ["transform.scaleX", "transform.scaleY"],
|
||||
} as const satisfies Record<string, ReadonlyArray<AnimationPropertyPath>>;
|
||||
|
||||
export type AnimationPropertyGroup = keyof typeof ANIMATION_PROPERTY_GROUPS;
|
||||
|
||||
export type VectorValue = { x: number; y: number };
|
||||
export type DiscreteValue = boolean | string;
|
||||
export type AnimationValue = number | string | boolean | VectorValue;
|
||||
export interface AnimationPropertyValueMap {
|
||||
"transform.positionX": number;
|
||||
"transform.positionY": number;
|
||||
"transform.scaleX": number;
|
||||
"transform.scaleY": number;
|
||||
"transform.rotate": number;
|
||||
opacity: number;
|
||||
volume: number;
|
||||
color: string;
|
||||
"background.color": string;
|
||||
"background.paddingX": number;
|
||||
"background.paddingY": number;
|
||||
"background.offsetX": number;
|
||||
"background.offsetY": number;
|
||||
"background.cornerRadius": number;
|
||||
}
|
||||
export type DynamicAnimationPathValue = number | string | boolean;
|
||||
|
||||
export interface NumericSpec {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
}
|
||||
export type AnimationValueForPath<TPath extends AnimationPath> =
|
||||
TPath extends AnimationPropertyPath
|
||||
? AnimationPropertyValueMap[TPath]
|
||||
: TPath extends GraphicParamPath | EffectParamPath
|
||||
? DynamicAnimationPathValue
|
||||
: never;
|
||||
export type AnimationNumericPropertyPath = {
|
||||
[K in AnimationPropertyPath]: AnimationValueForPath<K> extends number ? K : never;
|
||||
}[AnimationPropertyPath];
|
||||
export type AnimationColorPropertyPath = {
|
||||
[K in AnimationPropertyPath]: AnimationValueForPath<K> extends string ? K : never;
|
||||
}[AnimationPropertyPath];
|
||||
|
||||
export type ContinuousKeyframeInterpolation = "linear" | "hold" | "bezier";
|
||||
export type DiscreteKeyframeInterpolation = "hold";
|
||||
export type AnimationInterpolation =
|
||||
| ContinuousKeyframeInterpolation
|
||||
| DiscreteKeyframeInterpolation;
|
||||
|
||||
export type PrimitiveAnimationChannelKind = "scalar" | "discrete";
|
||||
export type AnimationBindingKind = "number" | "vector2" | "color" | "discrete";
|
||||
export type ScalarSegmentType = "step" | "linear" | "bezier";
|
||||
export type TangentMode = "auto" | "aligned" | "broken" | "flat";
|
||||
export type ChannelExtrapolationMode = "hold" | "linear";
|
||||
|
||||
export interface CurveHandle {
|
||||
dt: number;
|
||||
dv: number;
|
||||
}
|
||||
|
||||
interface BaseAnimationKeyframe<TValue extends number | DiscreteValue> {
|
||||
id: string;
|
||||
time: number; // relative to element start time
|
||||
value: TValue;
|
||||
}
|
||||
|
||||
export interface ScalarAnimationKey extends BaseAnimationKeyframe<number> {
|
||||
leftHandle?: CurveHandle;
|
||||
rightHandle?: CurveHandle;
|
||||
segmentToNext: ScalarSegmentType;
|
||||
tangentMode: TangentMode;
|
||||
}
|
||||
|
||||
export interface DiscreteAnimationKey
|
||||
extends BaseAnimationKeyframe<DiscreteValue> {}
|
||||
|
||||
export type AnimationKeyframe = ScalarAnimationKey | DiscreteAnimationKey;
|
||||
|
||||
export interface ScalarAnimationChannel {
|
||||
kind: "scalar";
|
||||
keys: ScalarAnimationKey[];
|
||||
extrapolation?: {
|
||||
before: ChannelExtrapolationMode;
|
||||
after: ChannelExtrapolationMode;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DiscreteAnimationChannel {
|
||||
kind: "discrete";
|
||||
keys: DiscreteAnimationKey[];
|
||||
}
|
||||
|
||||
export type AnimationChannel =
|
||||
| ScalarAnimationChannel
|
||||
| DiscreteAnimationChannel;
|
||||
|
||||
export type ElementAnimationChannelMap = Record<
|
||||
string,
|
||||
AnimationChannel | undefined
|
||||
>;
|
||||
|
||||
export interface AnimationBindingComponent<TKey extends string = string> {
|
||||
key: TKey;
|
||||
channelId: string;
|
||||
}
|
||||
|
||||
interface BaseAnimationBinding<
|
||||
TKind extends AnimationBindingKind,
|
||||
TComponentKey extends string,
|
||||
> {
|
||||
path: AnimationPath;
|
||||
kind: TKind;
|
||||
components: AnimationBindingComponent<TComponentKey>[];
|
||||
}
|
||||
|
||||
export interface NumberAnimationBinding
|
||||
extends BaseAnimationBinding<"number", "value"> {}
|
||||
|
||||
export interface Vector2AnimationBinding
|
||||
extends BaseAnimationBinding<"vector2", "x" | "y"> {}
|
||||
|
||||
export interface ColorAnimationBinding
|
||||
extends BaseAnimationBinding<"color", "r" | "g" | "b" | "a"> {
|
||||
colorSpace: "srgb-linear";
|
||||
}
|
||||
|
||||
export interface DiscreteAnimationBinding
|
||||
extends BaseAnimationBinding<"discrete", "value"> {}
|
||||
|
||||
export type AnimationBindingInstance =
|
||||
| NumberAnimationBinding
|
||||
| Vector2AnimationBinding
|
||||
| ColorAnimationBinding
|
||||
| DiscreteAnimationBinding;
|
||||
|
||||
export interface AnimationBindingByKind {
|
||||
number: NumberAnimationBinding;
|
||||
vector2: Vector2AnimationBinding;
|
||||
color: ColorAnimationBinding;
|
||||
discrete: DiscreteAnimationBinding;
|
||||
}
|
||||
|
||||
export type AnimationBindingOfKind<TKind extends AnimationBindingKind> =
|
||||
AnimationBindingByKind[TKind];
|
||||
|
||||
export type ElementAnimationBindingMap = Record<
|
||||
string,
|
||||
AnimationBindingInstance | undefined
|
||||
>;
|
||||
|
||||
export interface ElementAnimations {
|
||||
bindings: ElementAnimationBindingMap;
|
||||
channels: ElementAnimationChannelMap;
|
||||
}
|
||||
|
||||
export type NormalizedCubicBezier = [number, number, number, number];
|
||||
export {
|
||||
ANIMATION_PROPERTY_GROUPS,
|
||||
ANIMATION_PROPERTY_PATHS,
|
||||
} from "@/model/decorations/animations";
|
||||
|
||||
export interface ScalarGraphChannelTarget {
|
||||
propertyPath: AnimationPath;
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
import type { EditorCore } from "@/core";
|
||||
import type {
|
||||
AnimationInterpolation,
|
||||
AnimationPath,
|
||||
AnimationValue,
|
||||
ScalarCurveKeyframePatch,
|
||||
SelectedKeyframeRef,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationInterpolation, AnimationPath, AnimationValue } from "@/model/decorations/animations";
|
||||
import type { ScalarCurveKeyframePatch, 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,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 { ParamValues } from "@/model/decorations/param-values";
|
||||
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;
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from "@/animation";
|
||||
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 { AnimationPath, AnimationValue } from "@/model/decorations/animations";
|
||||
import type { SceneTracks, TimelineElement } from "@/model";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
|
||||
function removeKeyframeAndPersist({
|
||||
|
||||
@@ -2,8 +2,8 @@ import { EditorCore } from "@/core";
|
||||
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 { AnimationPath } from "@/model/decorations/animations";
|
||||
import type { SceneTracks } from "@/model";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
|
||||
export class RetimeKeyframeCommand extends Command {
|
||||
|
||||
@@ -5,11 +5,9 @@ import {
|
||||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { updateElementInSceneTracks } from "@/timeline";
|
||||
import { resolveAnimationTarget } from "@/timeline/animation-targets";
|
||||
import type {
|
||||
AnimationPath,
|
||||
ScalarCurveKeyframePatch,
|
||||
} from "@/animation/types";
|
||||
import type { SceneTracks } from "@/timeline";
|
||||
import type { AnimationPath } from "@/model/decorations/animations";
|
||||
import type { ScalarCurveKeyframePatch } from "@/animation/types";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class UpdateScalarKeyframeCurveCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
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 { AnimationInterpolation } from "@/model/decorations/animations";
|
||||
import type { SceneTracks } from "@/model";
|
||||
|
||||
export class UpsertEffectParamKeyframeCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
||||
@@ -2,13 +2,9 @@ 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,
|
||||
AnimationInterpolation,
|
||||
AnimationValue,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationInterpolation, AnimationPath, AnimationValue } from "@/model/decorations/animations";
|
||||
|
||||
export class UpsertKeyframeCommand extends Command {
|
||||
private savedState: SceneTracks | null = null;
|
||||
|
||||
@@ -4,9 +4,9 @@ import {
|
||||
getCustomMaskClosedStateAfterPointRemoval,
|
||||
removeCustomMaskPoints,
|
||||
} from "@/masks/custom-path";
|
||||
import type { CustomMask } from "@/masks/types";
|
||||
import type { CustomMask } from "@/model/decorations/mask";
|
||||
import { isMaskableElement, updateElementInSceneTracks } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/timeline";
|
||||
import type { MaskableElement, SceneTracks } from "@/model";
|
||||
|
||||
function deletePointsFromCustomMask({
|
||||
mask,
|
||||
|
||||
@@ -2,9 +2,9 @@ import { EditorCore } from "@/core";
|
||||
import { Command, type CommandResult } from "@/commands/base-command";
|
||||
import { insertPointOnCustomMaskSegment } from "@/masks/definitions/custom";
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type { CustomMask } from "@/masks/types";
|
||||
import type { CustomMask } from "@/model/decorations/mask";
|
||||
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,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 { Mask } from "@/model/decorations/mask";
|
||||
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 {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { ParamDefinition, NumberParamDefinition } from "@/params";
|
||||
import type { NumberParamDefinition, ParamDefinition } from "@/model/decorations/param-values";
|
||||
import {
|
||||
formatNumberForDisplay,
|
||||
getFractionDigitsForStep,
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ import {
|
||||
getKeyframeAtTime,
|
||||
hasKeyframesForPath,
|
||||
} from "@/animation";
|
||||
import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import type { AnimationPropertyPath, ElementAnimations } from "@/model/decorations/animations";
|
||||
import type { TimelineElement } from "@/model";
|
||||
import { upsertElementKeyframe } from "@/timeline/animation-properties";
|
||||
|
||||
export function useKeyframedColorProperty({
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ import {
|
||||
getKeyframeAtTime,
|
||||
hasKeyframesForPath,
|
||||
} from "@/animation";
|
||||
import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import type { AnimationPropertyPath, ElementAnimations } from "@/model/decorations/animations";
|
||||
import type { TimelineElement } from "@/model";
|
||||
import { upsertElementKeyframe } from "@/timeline/animation-properties";
|
||||
import { snapToStep } from "@/utils/math";
|
||||
import { usePropertyDraft } from "./use-property-draft";
|
||||
|
||||
+3
-5
@@ -7,16 +7,14 @@ import {
|
||||
hasKeyframesForPath,
|
||||
upsertPathKeyframe,
|
||||
} from "@/animation";
|
||||
import type {
|
||||
ElementAnimations,
|
||||
} from "@/animation/types";
|
||||
import type { ElementAnimations } from "@/model/decorations/animations";
|
||||
import {
|
||||
coerceAnimationParamValue,
|
||||
getAnimationParamDefaultInterpolation,
|
||||
getAnimationParamValueKind,
|
||||
} from "@/animation/animated-params";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { TimelineElement } from "@/timeline";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
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 { ParamValues } from "@/model/decorations/param-values";
|
||||
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";
|
||||
@@ -15,13 +9,8 @@ import {
|
||||
canElementBeHidden,
|
||||
canElementHaveAudio,
|
||||
} from "@/timeline/element-utils";
|
||||
import type {
|
||||
AnimationPath,
|
||||
AnimationInterpolation,
|
||||
AnimationValue,
|
||||
AnimationValueForPath,
|
||||
ScalarCurveKeyframePatch,
|
||||
} from "@/animation/types";
|
||||
import type { AnimationInterpolation, AnimationPath, AnimationValue, AnimationValueForPath } from "@/model/decorations/animations";
|
||||
import type { ScalarCurveKeyframePatch } from "@/animation/types";
|
||||
import {
|
||||
getElementLocalTime,
|
||||
resolveAnimationPathValueAtTime,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import type { ParamValues } from "@/params";
|
||||
import type { Effect } from "@/effects/types";
|
||||
import type { EffectElement, VisualElement } from "@/timeline";
|
||||
import type { ParamValues } from "@/model/decorations/param-values";
|
||||
import type { Effect } from "@/model/decorations/effect";
|
||||
import type { EffectElement, VisualElement } from "@/model";
|
||||
import { effectsRegistry } from "@/effects";
|
||||
import { useEditor } from "@/editor/use-editor";
|
||||
import { useElementPreview } from "@/timeline/hooks/use-element-preview";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { EffectDefinition, EffectPass } from "@/effects/types";
|
||||
import type { EffectPass } from "@/model/decorations/effect";
|
||||
import type { EffectDefinition } from "@/effects/types";
|
||||
|
||||
export const GAUSSIAN_BLUR_SHADER = "gaussian-blur";
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { generateUUID } from "@/utils/id";
|
||||
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 type { ParamValues } from "@/model/decorations/param-values";
|
||||
import type { Effect, EffectPass } from "@/model/decorations/effect";
|
||||
import type { EffectDefinition } from "@/effects/types";
|
||||
import { VISUAL_ELEMENT_TYPES } from "@/model";
|
||||
|
||||
export { effectsRegistry } from "./registry";
|
||||
export { registerDefaultEffects } from "./definitions";
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
import type { ParamDefinition, ParamValues } from "@/params";
|
||||
import type { ParamDefinition, ParamValues } from "@/model/decorations/param-values";
|
||||
import type { EffectPass, EffectPassTemplate } from "@/model/decorations/effect";
|
||||
|
||||
export interface Effect {
|
||||
id: string;
|
||||
type: string;
|
||||
params: ParamValues;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export type EffectUniformValue = number | number[];
|
||||
|
||||
export interface EffectPass {
|
||||
shader: string;
|
||||
uniforms: Record<string, EffectUniformValue>;
|
||||
}
|
||||
|
||||
export interface EffectPassTemplate {
|
||||
shader: string;
|
||||
uniforms(params: {
|
||||
effectParams: ParamValues;
|
||||
width: number;
|
||||
height: number;
|
||||
}): Record<string, EffectUniformValue>;
|
||||
}
|
||||
export type {
|
||||
Effect,
|
||||
EffectPass,
|
||||
EffectPassTemplate,
|
||||
EffectUniformValue,
|
||||
} from "@/model/decorations/effect";
|
||||
|
||||
export interface EffectRendererConfig {
|
||||
passes: EffectPassTemplate[];
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
useKeyframedParamProperty,
|
||||
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 { ParamDefinition, ParamValues } from "@/model/decorations/param-values";
|
||||
import type { GraphicElement } from "@/model";
|
||||
import {
|
||||
graphicsRegistry,
|
||||
registerDefaultGraphics,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import { applyAlignedStroke } from "../stroke";
|
||||
import { STROKE_ALIGN_PARAM, type GraphicStrokeAlign } from "./shared";
|
||||
import type { GraphicDefinition } from "../types";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import { applyAlignedStroke } from "../stroke";
|
||||
import { STROKE_ALIGN_PARAM, type GraphicStrokeAlign } from "./shared";
|
||||
import type { GraphicDefinition } from "../types";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import { applyAlignedStroke } from "../stroke";
|
||||
import { STROKE_ALIGN_PARAM, type GraphicStrokeAlign } from "./shared";
|
||||
import type { GraphicDefinition } from "../types";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
|
||||
export type GraphicStrokeAlign = "inside" | "center" | "outside";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import { applyAlignedStroke } from "../stroke";
|
||||
import { STROKE_ALIGN_PARAM, type GraphicStrokeAlign } from "./shared";
|
||||
import type { GraphicDefinition } from "../types";
|
||||
|
||||
+149
-149
@@ -1,149 +1,149 @@
|
||||
import { resolveGraphicParamsAtTime } from "@/animation";
|
||||
import type { ElementAnimations } from "@/animation/types";
|
||||
import { buildDefaultParamValues } from "@/params/registry";
|
||||
import type { ParamValues } from "@/params";
|
||||
import { graphicsRegistry } from "./registry";
|
||||
import {
|
||||
registerDefaultGraphics,
|
||||
ellipseGraphicDefinition,
|
||||
polygonGraphicDefinition,
|
||||
rectangleGraphicDefinition,
|
||||
starGraphicDefinition,
|
||||
} from "./definitions";
|
||||
import {
|
||||
DEFAULT_GRAPHIC_SOURCE_SIZE,
|
||||
type GraphicInstance,
|
||||
type GraphicDefinition,
|
||||
} from "./types";
|
||||
|
||||
const graphicPreviewUrlCache = new Map<string, string>();
|
||||
|
||||
const FALLBACK_CORNER_RADIUS_RATIO = 0.2;
|
||||
const FALLBACK_FILL_OPACITY = 0.08;
|
||||
const FALLBACK_MIN_FONT_SIZE = 12;
|
||||
const FALLBACK_FONT_SIZE_RATIO = 0.15;
|
||||
|
||||
function buildFallbackPreviewUrl({
|
||||
name,
|
||||
size,
|
||||
}: {
|
||||
name: string;
|
||||
size: number;
|
||||
}): string {
|
||||
const svg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
|
||||
<rect width="${size}" height="${size}" rx="${size * FALLBACK_CORNER_RADIUS_RATIO}" fill="white" fill-opacity="${FALLBACK_FILL_OPACITY}" />
|
||||
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="white" font-size="${Math.max(FALLBACK_MIN_FONT_SIZE, size * FALLBACK_FONT_SIZE_RATIO)}" font-family="sans-serif">${name}</text>
|
||||
</svg>
|
||||
`;
|
||||
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
||||
}
|
||||
|
||||
export function getGraphicDefinition({
|
||||
definitionId,
|
||||
}: {
|
||||
definitionId: string;
|
||||
}): GraphicDefinition {
|
||||
registerDefaultGraphics();
|
||||
return graphicsRegistry.get(definitionId);
|
||||
}
|
||||
|
||||
export function buildDefaultGraphicInstance({
|
||||
definitionId,
|
||||
}: {
|
||||
definitionId: string;
|
||||
}): GraphicInstance {
|
||||
const definition = getGraphicDefinition({ definitionId });
|
||||
return {
|
||||
definitionId,
|
||||
params: buildDefaultParamValues(definition.params),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGraphicParams(
|
||||
definition: GraphicDefinition,
|
||||
params?: ParamValues,
|
||||
): ParamValues {
|
||||
return {
|
||||
...buildDefaultParamValues(definition.params),
|
||||
...(params ?? {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGraphicElementParamsAtTime({
|
||||
element,
|
||||
localTime,
|
||||
}: {
|
||||
element: {
|
||||
definitionId: string;
|
||||
params: ParamValues;
|
||||
animations?: ElementAnimations;
|
||||
};
|
||||
localTime: number;
|
||||
}): ParamValues {
|
||||
const definition = getGraphicDefinition({
|
||||
definitionId: element.definitionId,
|
||||
});
|
||||
return resolveGraphicParamsAtTime({
|
||||
params: resolveGraphicParams(definition, element.params),
|
||||
definitions: definition.params,
|
||||
animations: element.animations,
|
||||
localTime,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildGraphicPreviewUrl({
|
||||
definitionId,
|
||||
params,
|
||||
size = DEFAULT_GRAPHIC_SOURCE_SIZE,
|
||||
}: {
|
||||
definitionId: string;
|
||||
params?: ParamValues;
|
||||
size?: number;
|
||||
}): string {
|
||||
const definition = getGraphicDefinition({ definitionId });
|
||||
const resolvedParams = resolveGraphicParams(definition, params);
|
||||
const cacheKey = JSON.stringify({ definitionId, resolvedParams, size });
|
||||
const cachedUrl = graphicPreviewUrlCache.get(cacheKey);
|
||||
if (cachedUrl) {
|
||||
return cachedUrl;
|
||||
}
|
||||
|
||||
if (typeof document === "undefined") {
|
||||
return buildFallbackPreviewUrl({ name: definition.name, size });
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
return buildFallbackPreviewUrl({ name: definition.name, size });
|
||||
}
|
||||
|
||||
definition.render({
|
||||
ctx,
|
||||
params: resolvedParams,
|
||||
width: size,
|
||||
height: size,
|
||||
});
|
||||
|
||||
const previewUrl = canvas.toDataURL("image/png");
|
||||
graphicPreviewUrlCache.set(cacheKey, previewUrl);
|
||||
return previewUrl;
|
||||
}
|
||||
|
||||
export {
|
||||
DEFAULT_GRAPHIC_SOURCE_SIZE,
|
||||
ellipseGraphicDefinition,
|
||||
graphicsRegistry,
|
||||
polygonGraphicDefinition,
|
||||
rectangleGraphicDefinition,
|
||||
registerDefaultGraphics,
|
||||
starGraphicDefinition,
|
||||
};
|
||||
export type {
|
||||
GraphicDefinition,
|
||||
GraphicInstance,
|
||||
GraphicRenderContext,
|
||||
} from "./types";
|
||||
import { resolveGraphicParamsAtTime } from "@/animation";
|
||||
import type { ElementAnimations } from "@/model/decorations/animations";
|
||||
import { buildDefaultParamValues } from "@/params/registry";
|
||||
import type { ParamValues } from "@/model/decorations/param-values";
|
||||
import { graphicsRegistry } from "./registry";
|
||||
import {
|
||||
registerDefaultGraphics,
|
||||
ellipseGraphicDefinition,
|
||||
polygonGraphicDefinition,
|
||||
rectangleGraphicDefinition,
|
||||
starGraphicDefinition,
|
||||
} from "./definitions";
|
||||
import {
|
||||
DEFAULT_GRAPHIC_SOURCE_SIZE,
|
||||
type GraphicInstance,
|
||||
type GraphicDefinition,
|
||||
} from "./types";
|
||||
|
||||
const graphicPreviewUrlCache = new Map<string, string>();
|
||||
|
||||
const FALLBACK_CORNER_RADIUS_RATIO = 0.2;
|
||||
const FALLBACK_FILL_OPACITY = 0.08;
|
||||
const FALLBACK_MIN_FONT_SIZE = 12;
|
||||
const FALLBACK_FONT_SIZE_RATIO = 0.15;
|
||||
|
||||
function buildFallbackPreviewUrl({
|
||||
name,
|
||||
size,
|
||||
}: {
|
||||
name: string;
|
||||
size: number;
|
||||
}): string {
|
||||
const svg = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
|
||||
<rect width="${size}" height="${size}" rx="${size * FALLBACK_CORNER_RADIUS_RATIO}" fill="white" fill-opacity="${FALLBACK_FILL_OPACITY}" />
|
||||
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" fill="white" font-size="${Math.max(FALLBACK_MIN_FONT_SIZE, size * FALLBACK_FONT_SIZE_RATIO)}" font-family="sans-serif">${name}</text>
|
||||
</svg>
|
||||
`;
|
||||
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
||||
}
|
||||
|
||||
export function getGraphicDefinition({
|
||||
definitionId,
|
||||
}: {
|
||||
definitionId: string;
|
||||
}): GraphicDefinition {
|
||||
registerDefaultGraphics();
|
||||
return graphicsRegistry.get(definitionId);
|
||||
}
|
||||
|
||||
export function buildDefaultGraphicInstance({
|
||||
definitionId,
|
||||
}: {
|
||||
definitionId: string;
|
||||
}): GraphicInstance {
|
||||
const definition = getGraphicDefinition({ definitionId });
|
||||
return {
|
||||
definitionId,
|
||||
params: buildDefaultParamValues(definition.params),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGraphicParams(
|
||||
definition: GraphicDefinition,
|
||||
params?: ParamValues,
|
||||
): ParamValues {
|
||||
return {
|
||||
...buildDefaultParamValues(definition.params),
|
||||
...(params ?? {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveGraphicElementParamsAtTime({
|
||||
element,
|
||||
localTime,
|
||||
}: {
|
||||
element: {
|
||||
definitionId: string;
|
||||
params: ParamValues;
|
||||
animations?: ElementAnimations;
|
||||
};
|
||||
localTime: number;
|
||||
}): ParamValues {
|
||||
const definition = getGraphicDefinition({
|
||||
definitionId: element.definitionId,
|
||||
});
|
||||
return resolveGraphicParamsAtTime({
|
||||
params: resolveGraphicParams(definition, element.params),
|
||||
definitions: definition.params,
|
||||
animations: element.animations,
|
||||
localTime,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildGraphicPreviewUrl({
|
||||
definitionId,
|
||||
params,
|
||||
size = DEFAULT_GRAPHIC_SOURCE_SIZE,
|
||||
}: {
|
||||
definitionId: string;
|
||||
params?: ParamValues;
|
||||
size?: number;
|
||||
}): string {
|
||||
const definition = getGraphicDefinition({ definitionId });
|
||||
const resolvedParams = resolveGraphicParams(definition, params);
|
||||
const cacheKey = JSON.stringify({ definitionId, resolvedParams, size });
|
||||
const cachedUrl = graphicPreviewUrlCache.get(cacheKey);
|
||||
if (cachedUrl) {
|
||||
return cachedUrl;
|
||||
}
|
||||
|
||||
if (typeof document === "undefined") {
|
||||
return buildFallbackPreviewUrl({ name: definition.name, size });
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = size;
|
||||
canvas.height = size;
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) {
|
||||
return buildFallbackPreviewUrl({ name: definition.name, size });
|
||||
}
|
||||
|
||||
definition.render({
|
||||
ctx,
|
||||
params: resolvedParams,
|
||||
width: size,
|
||||
height: size,
|
||||
});
|
||||
|
||||
const previewUrl = canvas.toDataURL("image/png");
|
||||
graphicPreviewUrlCache.set(cacheKey, previewUrl);
|
||||
return previewUrl;
|
||||
}
|
||||
|
||||
export {
|
||||
DEFAULT_GRAPHIC_SOURCE_SIZE,
|
||||
ellipseGraphicDefinition,
|
||||
graphicsRegistry,
|
||||
polygonGraphicDefinition,
|
||||
rectangleGraphicDefinition,
|
||||
registerDefaultGraphics,
|
||||
starGraphicDefinition,
|
||||
};
|
||||
export type {
|
||||
GraphicDefinition,
|
||||
GraphicInstance,
|
||||
GraphicRenderContext,
|
||||
} from "./types";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParamDefinition, ParamValues } from "@/params";
|
||||
import type { ParamDefinition, ParamValues } from "@/model/decorations/param-values";
|
||||
|
||||
export const DEFAULT_GRAPHIC_SOURCE_SIZE = 512;
|
||||
|
||||
|
||||
@@ -15,12 +15,7 @@ import { textMaskDefinition } from "@/masks/definitions/text";
|
||||
import { getMaskSnapGeometry } from "@/masks/geometry";
|
||||
import { snapMaskInteraction } from "@/masks/snap";
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type {
|
||||
CustomMaskParams,
|
||||
RectangleMaskParams,
|
||||
SplitMaskParams,
|
||||
TextMaskParams,
|
||||
} from "@/masks/types";
|
||||
import type { CustomMaskParams, RectangleMaskParams, SplitMaskParams, TextMaskParams } from "@/model/decorations/mask";
|
||||
|
||||
const bounds: ElementBounds = {
|
||||
cx: 200,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import type { MaskableElement } from "@/timeline";
|
||||
import type { Mask, MaskType, TextMask } from "@/masks/types";
|
||||
import type { NumberParamDefinition, SelectParamDefinition } from "@/params";
|
||||
import type { MaskableElement } from "@/model";
|
||||
import type { Mask, MaskType, TextMask } from "@/model/decorations/mask";
|
||||
import type { NumberParamDefinition, SelectParamDefinition } from "@/model/decorations/param-values";
|
||||
import { masksRegistry, buildDefaultMaskInstance } from "@/masks";
|
||||
import { useEditor } from "@/editor/use-editor";
|
||||
import { useElementPreview } from "@/timeline/hooks/use-element-preview";
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
|
||||
export interface CustomMaskPathPoint {
|
||||
id: string;
|
||||
x: number;
|
||||
y: number;
|
||||
inX: number;
|
||||
inY: number;
|
||||
outX: number;
|
||||
outY: number;
|
||||
}
|
||||
import type { CustomMaskPathPoint } from "@/model/decorations/mask";
|
||||
export type { CustomMaskPathPoint } from "@/model/decorations/mask";
|
||||
|
||||
export type CustomMaskHandlePart = "anchor" | "in" | "out";
|
||||
|
||||
|
||||
@@ -3,15 +3,9 @@ import {
|
||||
MIN_MASK_DIMENSION,
|
||||
} from "@/masks/dimensions";
|
||||
import { computeFeatherUpdate } from "../param-update";
|
||||
import type {
|
||||
BaseMaskParams,
|
||||
MaskDefaultContext,
|
||||
MaskFeatures,
|
||||
MaskInteractionDefinition,
|
||||
MaskParamUpdateArgs,
|
||||
RectangleMaskParams,
|
||||
} from "@/masks/types";
|
||||
import type { NumberParamDefinition, ParamDefinition } from "@/params";
|
||||
import type { BaseMaskParams, RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefaultContext, MaskFeatures, MaskInteractionDefinition, MaskParamUpdateArgs } from "@/masks/types";
|
||||
import type { NumberParamDefinition, ParamDefinition } from "@/model/decorations/param-values";
|
||||
import {
|
||||
getBoxMaskHandlePositions,
|
||||
getBoxMaskOverlays,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type {
|
||||
MaskDefaultContext,
|
||||
MaskDefinition,
|
||||
RectangleMaskParams,
|
||||
} from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefaultContext, MaskDefinition } from "@/masks/types";
|
||||
import {
|
||||
BOX_LIKE_MASK_PARAMS,
|
||||
buildBoxMaskInteraction,
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { generateUUID } from "@/utils/id";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import { PEN_CURSOR } from "@/preview/components/cursors";
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type {
|
||||
CustomMask,
|
||||
CustomMaskParams,
|
||||
MaskDefinition,
|
||||
MaskHandlePosition,
|
||||
MaskOverlay,
|
||||
MaskParamUpdateArgs,
|
||||
} from "@/masks/types";
|
||||
import type { CustomMask, CustomMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition, MaskHandlePosition, MaskOverlay, MaskParamUpdateArgs } from "@/masks/types";
|
||||
import {
|
||||
buildCustomMaskPath2D,
|
||||
buildCustomMaskSvgPath,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MaskDefinition, RectangleMaskParams } from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition } from "@/masks/types";
|
||||
import {
|
||||
BOX_LIKE_MASK_PARAMS,
|
||||
buildBoxMaskInteraction,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MaskDefinition, RectangleMaskParams } from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition } from "@/masks/types";
|
||||
import {
|
||||
BOX_LIKE_MASK_PARAMS,
|
||||
buildBoxMaskInteraction,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MaskDefinition, RectangleMaskParams } from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition } from "@/masks/types";
|
||||
import {
|
||||
BOX_LIKE_MASK_PARAMS,
|
||||
buildBoxMaskInteraction,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { BaseMaskParams, MaskDefinition } from "@/masks/types";
|
||||
import type { BaseMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition } from "@/masks/types";
|
||||
import { masksRegistry, type MaskIconProps } from "../registry";
|
||||
import { cinematicBarsMaskDefinition } from "./cinematic-bars";
|
||||
import { customMaskDefinition } from "./custom";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MaskDefinition, RectangleMaskParams } from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition } from "@/masks/types";
|
||||
import {
|
||||
BOX_LIKE_MASK_PARAMS,
|
||||
buildBoxMaskInteraction,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { computeFeatherUpdate } from "../param-update";
|
||||
import type {
|
||||
MaskDefinition,
|
||||
MaskParamUpdateArgs,
|
||||
SplitMaskParams,
|
||||
} from "@/masks/types";
|
||||
import type { SplitMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition, MaskParamUpdateArgs } from "@/masks/types";
|
||||
import { halfPlaneSign, lineEdgeIntersection } from "../utils";
|
||||
import {
|
||||
getLineMaskHandlePositions,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { MaskDefinition, RectangleMaskParams } from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition } from "@/masks/types";
|
||||
import {
|
||||
BOX_LIKE_MASK_PARAMS,
|
||||
buildBoxMaskInteraction,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type {
|
||||
MaskDefinition,
|
||||
MaskParamUpdateArgs,
|
||||
TextMask,
|
||||
TextMaskParams,
|
||||
} from "@/masks/types";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import type { TextMask, TextMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskDefinition, MaskParamUpdateArgs } from "@/masks/types";
|
||||
import { DEFAULTS } from "@/timeline/defaults";
|
||||
import { MIN_FONT_SIZE, MAX_FONT_SIZE } from "@/text/typography";
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
|
||||
type CenterMaskParams = {
|
||||
centerX: number;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import { FEATHER_HANDLE_SCALE } from "@/masks/feather";
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type {
|
||||
MaskFeatures,
|
||||
MaskHandlePosition,
|
||||
MaskLineOverlay,
|
||||
MaskOverlay,
|
||||
MaskRectOverlay,
|
||||
RectangleMaskParams,
|
||||
MaskShapeOverlay,
|
||||
} from "@/masks/types";
|
||||
import type { RectangleMaskParams } from "@/model/decorations/mask";
|
||||
import type { MaskFeatures, MaskHandlePosition, MaskLineOverlay, MaskOverlay, MaskRectOverlay, MaskShapeOverlay } from "@/masks/types";
|
||||
|
||||
const LINE_HANDLE_OFFSET_SCREEN_PX = 20;
|
||||
const BOX_HANDLE_OFFSET_SCREEN_PX = 20;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Mask, MaskDefaultContext, MaskType } from "@/masks/types";
|
||||
import type { Mask, MaskType } from "@/model/decorations/mask";
|
||||
import type { MaskDefaultContext } from "@/masks/types";
|
||||
import { masksRegistry } from "./registry";
|
||||
import { generateUUID } from "@/utils/id";
|
||||
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import { MAX_FEATHER } from "@/masks/feather";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type {
|
||||
BaseMaskParams,
|
||||
MaskDefaultContext,
|
||||
MaskDefinition,
|
||||
MaskInteractionResult,
|
||||
MaskSnapArgs,
|
||||
MaskSnapResult,
|
||||
MaskType,
|
||||
} from "@/masks/types";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import type { BaseMaskParams, MaskType } from "@/model/decorations/mask";
|
||||
import type { MaskDefaultContext, MaskDefinition, MaskInteractionResult, MaskSnapArgs, MaskSnapResult } from "@/masks/types";
|
||||
import type { HugeiconsIconProps } from "@hugeicons/react";
|
||||
import { DefinitionRegistry } from "@/params/registry";
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
snapScale,
|
||||
snapScaleAxes,
|
||||
type ScaleEdgePreference,
|
||||
type SnapLine,
|
||||
type PreviewSnapLine,
|
||||
} from "@/preview/preview-snap";
|
||||
import type { RectangleMaskParams, SplitMaskParams } from "@/masks/types";
|
||||
import type { RectangleMaskParams, SplitMaskParams } from "@/model/decorations/mask";
|
||||
import {
|
||||
isRectangleMaskParams,
|
||||
getMaskSnapGeometry,
|
||||
@@ -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([
|
||||
|
||||
+27
-128
@@ -1,135 +1,34 @@
|
||||
import type { ElementBounds } from "@/preview/element-bounds";
|
||||
import type { SnapLine } from "@/preview/preview-snap";
|
||||
import type { ParamDefinition } from "@/params";
|
||||
import type { CustomMaskPathPoint } from "@/masks/custom-path";
|
||||
import type { PreviewSnapLine } from "@/preview/preview-snap";
|
||||
import type { ParamDefinition } from "@/model/decorations/param-values";
|
||||
import type {
|
||||
BaseMaskParams,
|
||||
Mask,
|
||||
MaskType,
|
||||
} from "@/model/decorations/mask";
|
||||
|
||||
export type {
|
||||
BaseMaskParams,
|
||||
CinematicBarsMask,
|
||||
CustomMask,
|
||||
CustomMaskParams,
|
||||
CustomMaskPathPoint,
|
||||
DiamondMask,
|
||||
EllipseMask,
|
||||
HeartMask,
|
||||
Mask,
|
||||
MaskType,
|
||||
RectangleMask,
|
||||
RectangleMaskParams,
|
||||
SplitMask,
|
||||
SplitMaskParams,
|
||||
StarMask,
|
||||
TextDecoration,
|
||||
TextFontStyle,
|
||||
TextFontWeight,
|
||||
} from "@/text/primitives";
|
||||
|
||||
export type MaskType =
|
||||
| "split"
|
||||
| "cinematic-bars"
|
||||
| "rectangle"
|
||||
| "ellipse"
|
||||
| "heart"
|
||||
| "diamond"
|
||||
| "star"
|
||||
| "text"
|
||||
| "custom";
|
||||
|
||||
export interface BaseMaskParams {
|
||||
feather: number;
|
||||
inverted: boolean;
|
||||
strokeColor: string;
|
||||
strokeWidth: number;
|
||||
strokeAlign: "inside" | "center" | "outside";
|
||||
}
|
||||
|
||||
export interface SplitMaskParams extends BaseMaskParams {
|
||||
centerX: number;
|
||||
centerY: number;
|
||||
rotation: number;
|
||||
}
|
||||
|
||||
export interface RectangleMaskParams extends BaseMaskParams {
|
||||
centerX: number;
|
||||
centerY: number;
|
||||
width: number;
|
||||
height: number;
|
||||
rotation: number;
|
||||
scale: number;
|
||||
}
|
||||
|
||||
export interface TextMaskParams extends BaseMaskParams {
|
||||
content: string;
|
||||
fontSize: number;
|
||||
fontFamily: string;
|
||||
fontWeight: TextFontWeight;
|
||||
fontStyle: TextFontStyle;
|
||||
textDecoration: TextDecoration;
|
||||
letterSpacing: number;
|
||||
lineHeight: number;
|
||||
centerX: number;
|
||||
centerY: number;
|
||||
rotation: number;
|
||||
scale: number;
|
||||
}
|
||||
|
||||
export interface CustomMaskParams extends BaseMaskParams {
|
||||
path: CustomMaskPathPoint[];
|
||||
closed: boolean;
|
||||
centerX: number;
|
||||
centerY: number;
|
||||
rotation: number;
|
||||
scale: number;
|
||||
}
|
||||
|
||||
export interface SplitMask {
|
||||
id: string;
|
||||
type: "split";
|
||||
params: SplitMaskParams;
|
||||
}
|
||||
|
||||
export interface CinematicBarsMask {
|
||||
id: string;
|
||||
type: "cinematic-bars";
|
||||
params: RectangleMaskParams;
|
||||
}
|
||||
|
||||
export interface RectangleMask {
|
||||
id: string;
|
||||
type: "rectangle";
|
||||
params: RectangleMaskParams;
|
||||
}
|
||||
|
||||
export interface EllipseMask {
|
||||
id: string;
|
||||
type: "ellipse";
|
||||
params: RectangleMaskParams;
|
||||
}
|
||||
|
||||
export interface HeartMask {
|
||||
id: string;
|
||||
type: "heart";
|
||||
params: RectangleMaskParams;
|
||||
}
|
||||
|
||||
export interface DiamondMask {
|
||||
id: string;
|
||||
type: "diamond";
|
||||
params: RectangleMaskParams;
|
||||
}
|
||||
|
||||
export interface StarMask {
|
||||
id: string;
|
||||
type: "star";
|
||||
params: RectangleMaskParams;
|
||||
}
|
||||
|
||||
export interface TextMask {
|
||||
id: string;
|
||||
type: "text";
|
||||
params: TextMaskParams;
|
||||
}
|
||||
|
||||
export interface CustomMask {
|
||||
id: string;
|
||||
type: "custom";
|
||||
params: CustomMaskParams;
|
||||
}
|
||||
|
||||
export type Mask =
|
||||
| SplitMask
|
||||
| CinematicBarsMask
|
||||
| RectangleMask
|
||||
| EllipseMask
|
||||
| HeartMask
|
||||
| DiamondMask
|
||||
| StarMask
|
||||
| TextMask
|
||||
| CustomMask;
|
||||
TextMask,
|
||||
TextMaskParams,
|
||||
} from "@/model/decorations/mask";
|
||||
|
||||
export interface MaskRenderer {
|
||||
buildPath?: (params: {
|
||||
@@ -261,7 +160,7 @@ export interface MaskSnapResult<
|
||||
TParams extends BaseMaskParams = BaseMaskParams,
|
||||
> {
|
||||
params: TParams;
|
||||
activeLines: SnapLine[];
|
||||
activeLines: PreviewSnapLine[];
|
||||
}
|
||||
|
||||
export interface MaskInteractionResult {
|
||||
|
||||
@@ -14,11 +14,12 @@ 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 { Mask } from "@/model/decorations/mask";
|
||||
import type { MaskInteractionResult } from "@/masks/types";
|
||||
import type { MaskableElement } from "@/model";
|
||||
import { registerCanceller } from "@/editor/cancel-interaction";
|
||||
|
||||
interface DragState {
|
||||
@@ -94,7 +95,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 +553,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 +561,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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user