refactor: extract decoration model types

Move animation, effect, mask, and param value shapes into model decorations so element data no longer depends on decoration domains.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Maze Winther 2026-05-03 03:58:21 +02:00
parent 6ac2116daa
commit ab982b888e
102 changed files with 633 additions and 659 deletions

View File

@ -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({

View File

@ -1,4 +1,4 @@
import type { ScalarAnimationKey } from "@/animation/types";
import type { ScalarAnimationKey } from "@/model/decorations/animations";
const BEZIER_SOLVE_ITERATIONS = 20;

View File

@ -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 {

View File

@ -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;

View File

@ -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";

View File

@ -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;

View File

@ -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.";

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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";

View File

@ -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 {

View File

@ -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,

View File

@ -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;

View File

@ -1,11 +1,6 @@
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 "@/model";

View File

@ -1,7 +1,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 { ParamValues } from "@/model/decorations/param-values";
import type { SceneTracks, VisualElement } from "@/model";
function updateEffectParamsOnElement({

View File

@ -5,7 +5,7 @@ import {
} from "@/animation";
import { Command, type CommandResult } from "@/commands/base-command";
import { updateElementInSceneTracks } from "@/timeline";
import type { AnimationPath, AnimationValue } from "@/animation/types";
import type { AnimationPath, AnimationValue } from "@/model/decorations/animations";
import type { SceneTracks, TimelineElement } from "@/model";
import { resolveAnimationTarget } from "@/timeline/animation-targets";

View File

@ -2,7 +2,7 @@ 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 { AnimationPath } from "@/model/decorations/animations";
import type { SceneTracks } from "@/model";
import { resolveAnimationTarget } from "@/timeline/animation-targets";

View File

@ -5,10 +5,8 @@ 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 { AnimationPath } from "@/model/decorations/animations";
import type { ScalarCurveKeyframePatch } from "@/animation/types";
import type { SceneTracks } from "@/model";
export class UpdateScalarKeyframeCurveCommand extends Command {

View File

@ -7,7 +7,7 @@ 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 { AnimationInterpolation } from "@/model/decorations/animations";
import type { SceneTracks } from "@/model";
export class UpsertEffectParamKeyframeCommand extends Command {

View File

@ -4,11 +4,7 @@ import { upsertPathKeyframe } from "@/animation";
import { updateElementInSceneTracks } 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;

View File

@ -4,7 +4,7 @@ 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 "@/model";

View File

@ -2,7 +2,7 @@ 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 "@/model";

View File

@ -1,7 +1,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 { Mask } from "@/model/decorations/mask";
import type { MaskableElement, SceneTracks } from "@/model";
export function toggleMaskInvertedOnElement({

View File

@ -1,6 +1,6 @@
"use client";
import type { ParamDefinition, NumberParamDefinition } from "@/params";
import type { NumberParamDefinition, ParamDefinition } from "@/model/decorations/param-values";
import {
formatNumberForDisplay,
getFractionDigitsForStep,

View File

@ -3,7 +3,7 @@ import {
getKeyframeAtTime,
hasKeyframesForPath,
} from "@/animation";
import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types";
import type { AnimationPropertyPath, ElementAnimations } from "@/model/decorations/animations";
import type { TimelineElement } from "@/model";
import { upsertElementKeyframe } from "@/timeline/animation-properties";

View File

@ -3,7 +3,7 @@ import {
getKeyframeAtTime,
hasKeyframesForPath,
} from "@/animation";
import type { AnimationPropertyPath, ElementAnimations } from "@/animation/types";
import type { AnimationPropertyPath, ElementAnimations } from "@/model/decorations/animations";
import type { TimelineElement } from "@/model";
import { upsertElementKeyframe } from "@/timeline/animation-properties";
import { snapToStep } from "@/utils/math";

View File

@ -7,15 +7,13 @@ 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 { ParamDefinition } from "@/model/decorations/param-values";
import type { TimelineElement } from "@/model";
export interface KeyframedParamPropertyResult {

View File

@ -1,6 +1,6 @@
import type { EditorCore } from "@/core";
import type { ElementBounds } from "@/preview/element-bounds";
import type { ParamValues } from "@/params";
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";
@ -9,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,

View File

@ -1,8 +1,8 @@
"use client";
import { useState } from "react";
import type { ParamValues } from "@/params";
import type { Effect } from "@/effects/types";
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";

View File

@ -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";

View File

@ -1,8 +1,9 @@
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 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";

View File

@ -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[];

View File

@ -6,7 +6,7 @@ import {
useKeyframedParamProperty,
type KeyframedParamPropertyResult,
} from "@/components/editor/panels/properties/hooks/use-keyframed-param-property";
import type { ParamDefinition, ParamValues } from "@/params";
import type { ParamDefinition, ParamValues } from "@/model/decorations/param-values";
import type { GraphicElement } from "@/model";
import {
graphicsRegistry,

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -1,4 +1,4 @@
import type { ParamDefinition } from "@/params";
import type { ParamDefinition } from "@/model/decorations/param-values";
export type GraphicStrokeAlign = "inside" | "center" | "outside";

View File

@ -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";

View File

@ -1,7 +1,7 @@
import { resolveGraphicParamsAtTime } from "@/animation";
import type { ElementAnimations } from "@/animation/types";
import type { ElementAnimations } from "@/model/decorations/animations";
import { buildDefaultParamValues } from "@/params/registry";
import type { ParamValues } from "@/params";
import type { ParamValues } from "@/model/decorations/param-values";
import { graphicsRegistry } from "./registry";
import {
registerDefaultGraphics,

View File

@ -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;

View File

@ -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,

View File

@ -1,8 +1,8 @@
"use client";
import type { MaskableElement } from "@/model";
import type { Mask, MaskType, TextMask } from "@/masks/types";
import type { NumberParamDefinition, SelectParamDefinition } from "@/params";
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";

View File

@ -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";

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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";

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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 {

View File

@ -1,7 +1,7 @@
import type { ElementBounds } from "@/preview/element-bounds";
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;

View File

@ -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;

View File

@ -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";

View File

@ -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";

View File

@ -8,7 +8,7 @@ import {
type ScaleEdgePreference,
type PreviewSnapLine,
} from "@/preview/preview-snap";
import type { RectangleMaskParams, SplitMaskParams } from "@/masks/types";
import type { RectangleMaskParams, SplitMaskParams } from "@/model/decorations/mask";
import {
isRectangleMaskParams,
getMaskSnapGeometry,

View File

@ -1,135 +1,34 @@
import type { ElementBounds } from "@/preview/element-bounds";
import type { PreviewSnapLine } from "@/preview/preview-snap";
import type { ParamDefinition } from "@/params";
import type { CustomMaskPathPoint } from "@/masks/custom-path";
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: {

View File

@ -17,7 +17,8 @@ import {
type PreviewSnapLine,
} from "@/preview/preview-snap";
import type { SelectedMaskPointSelection } from "@/selection/editor-selection";
import type { Mask, MaskInteractionResult } from "@/masks/types";
import type { Mask } from "@/model/decorations/mask";
import type { MaskInteractionResult } from "@/masks/types";
import type { MaskableElement } from "@/model";
import { registerCanceller } from "@/editor/cancel-interaction";

View File

@ -0,0 +1,183 @@
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;
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 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];

View File

@ -0,0 +1,24 @@
import type { ParamValues } from "./param-values";
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>;
}

View File

@ -0,0 +1,4 @@
export * from "./animations";
export * from "./effect";
export * from "./mask";
export * from "./param-values";

View File

@ -0,0 +1,136 @@
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 type TextFontWeight = "normal" | "bold";
export type TextFontStyle = "normal" | "italic";
export type TextDecoration = "none" | "underline" | "line-through";
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 CustomMaskPathPoint {
id: string;
x: number;
y: number;
inX: number;
inY: number;
outX: number;
outY: 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;

View File

@ -0,0 +1,49 @@
export type ParamValues = Record<string, number | string | boolean>;
export type ParamGroup = "stroke";
interface BaseParamDefinition<TKey extends string = string> {
key: TKey;
label: string;
group?: ParamGroup;
}
export interface NumberParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "number";
default: number;
min: number;
max?: number;
step: number;
/** When set, min/max/step are in display space. display = stored * displayMultiplier. */
displayMultiplier?: number;
/** Show as percentage of max. min/max/step/default stay in stored space. */
unit?: "percent";
/** Short label shown as the scrub handle icon in the number field (e.g. "W", "R"). */
shortLabel?: string;
}
export interface BooleanParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "boolean";
default: boolean;
}
export interface ColorParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "color";
default: string;
}
export interface SelectParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "select";
default: string;
options: Array<{ value: string; label: string }>;
}
export type ParamDefinition<TKey extends string = string> =
| NumberParamDefinition<TKey>
| BooleanParamDefinition<TKey>
| ColorParamDefinition<TKey>
| SelectParamDefinition<TKey>;

View File

@ -1,4 +1,4 @@
import type { ElementAnimations } from "@/animation/types";
import type { ElementAnimations } from "@/model/decorations/animations";
import type { ElementId } from "../ids";
export interface BaseTimelineElement {

View File

@ -1,4 +1,4 @@
import type { ParamValues } from "@/params";
import type { ParamValues } from "@/model/decorations/param-values";
import type { BaseTimelineElement } from "./base";
export interface EffectElement extends BaseTimelineElement {

View File

@ -1,6 +1,6 @@
import type { Effect } from "@/effects/types";
import type { Mask } from "@/masks/types";
import type { ParamValues } from "@/params";
import type { Effect } from "@/model/decorations/effect";
import type { Mask } from "@/model/decorations/mask";
import type { ParamValues } from "@/model/decorations/param-values";
import type { BlendMode, Transform } from "@/rendering";
import type { BaseTimelineElement } from "./base";

View File

@ -1,5 +1,5 @@
import type { Effect } from "@/effects/types";
import type { Mask } from "@/masks/types";
import type { Effect } from "@/model/decorations/effect";
import type { Mask } from "@/model/decorations/mask";
import type { BlendMode, Transform } from "@/rendering";
import type { BaseTimelineElement } from "./base";

View File

@ -1,4 +1,4 @@
import type { Effect } from "@/effects/types";
import type { Effect } from "@/model/decorations/effect";
import type { BlendMode, Transform } from "@/rendering";
import type { BaseTimelineElement } from "./base";

View File

@ -1,4 +1,4 @@
import type { Effect } from "@/effects/types";
import type { Effect } from "@/model/decorations/effect";
import type { BlendMode, Transform } from "@/rendering";
import type { BaseTimelineElement } from "./base";

View File

@ -1,5 +1,5 @@
import type { Effect } from "@/effects/types";
import type { Mask } from "@/masks/types";
import type { Effect } from "@/model/decorations/effect";
import type { Mask } from "@/model/decorations/mask";
import type { BlendMode, Transform } from "@/rendering";
import type { BaseTimelineElement, RetimeConfig } from "./base";

View File

@ -1,4 +1,5 @@
export * from "./bookmark";
export * from "./decorations";
export * from "./element-ref";
export * from "./elements";
export * from "./ids";

View File

@ -1,50 +1,10 @@
export type ParamValues = Record<string, number | string | boolean>;
export type ParamGroup = "stroke";
interface BaseParamDefinition<TKey extends string = string> {
key: TKey;
label: string;
group?: ParamGroup;
}
export interface NumberParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "number";
default: number;
min: number;
max?: number;
step: number;
/** When set, min/max/step are in display space. display = stored * displayMultiplier. */
displayMultiplier?: number;
/** Show as percentage of max. min/max/step/default stay in stored space. */
unit?: "percent";
/** Short label shown as the scrub handle icon in the number field (e.g. "W", "R"). */
shortLabel?: string;
}
export interface BooleanParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "boolean";
default: boolean;
}
export interface ColorParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "color";
default: string;
}
export interface SelectParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "select";
default: string;
options: Array<{ value: string; label: string }>;
}
export type ParamDefinition<TKey extends string = string> =
| NumberParamDefinition<TKey>
| BooleanParamDefinition<TKey>
| ColorParamDefinition<TKey>
| SelectParamDefinition<TKey>;
export type {
BooleanParamDefinition,
ColorParamDefinition,
NumberParamDefinition,
ParamDefinition,
ParamGroup,
ParamValues,
SelectParamDefinition,
} from "@/model/decorations/param-values";

View File

@ -1,4 +1,4 @@
import type { ParamDefinition, ParamValues } from "@/params";
import type { ParamDefinition, ParamValues } from "@/model/decorations/param-values";
export function buildDefaultParamValues(
params: ParamDefinition[],

View File

@ -22,7 +22,7 @@ import {
hasKeyframesForPath,
setChannel,
} from "@/animation";
import type { ElementAnimations } from "@/animation/types";
import type { ElementAnimations } from "@/model/decorations/animations";
import type { Transform } from "@/rendering";
import { resolveTransformAtTime } from "@/rendering/animation-values";
import type { ElementRef, SceneTracks, TimelineElement, VisualElement } from "@/model";

View File

@ -1,4 +1,4 @@
import type { ElementAnimations } from "@/animation/types";
import type { ElementAnimations } from "@/model/decorations/animations";
import { resolveAnimationPathValueAtTime } from "@/animation";
import type { Transform } from "./index";

View File

@ -21,7 +21,7 @@ import {
} from "@/components/ui/select";
import type { BlendMode } from "@/rendering";
import type { ElementType } from "@/model";
import type { ElementAnimations } from "@/animation/types";
import type { ElementAnimations } from "@/model/decorations/animations";
import { HugeiconsIcon } from "@hugeicons/react";
import { RainDropIcon } from "@hugeicons/core-free-icons";
import { KeyframeToggle } from "@/components/editor/panels/properties/components/keyframe-toggle";

View File

@ -1,5 +1,5 @@
import type { BlendMode } from "@/rendering";
import type { EffectPass } from "@/effects/types";
import type { EffectPass } from "@/model/decorations/effect";
export type FrameDescriptor = {
width: number;

View File

@ -1,7 +1,7 @@
import { createOffscreenCanvas } from "./canvas-utils";
import { effectsRegistry, resolveEffectPasses } from "@/effects";
import { buildDefaultParamValues } from "@/params/registry";
import type { ParamValues } from "@/params";
import type { ParamValues } from "@/model/decorations/param-values";
import { gpuRenderer } from "./gpu-renderer";
const PREVIEW_SIZE = 160;

View File

@ -3,7 +3,7 @@ import {
applyMaskFeather as applyMaskFeatherWasm,
initializeGpu,
} from "opencut-wasm";
import type { EffectPass, EffectUniformValue } from "@/effects/types";
import type { EffectPass, EffectUniformValue } from "@/model/decorations/effect";
let gpuAvailable = false;
let initPromise: Promise<void> | null = null;

View File

@ -1,4 +1,4 @@
import type { EffectPass } from "@/effects/types";
import type { EffectPass } from "@/model/decorations/effect";
import type { RetimeConfig } from "@/model";
import { BaseNode } from "./base-node";

View File

@ -1,5 +1,5 @@
import type { EffectPass } from "@/effects/types";
import type { ParamValues } from "@/params";
import type { EffectPass } from "@/model/decorations/effect";
import type { ParamValues } from "@/model/decorations/param-values";
import { BaseNode } from "./base-node";
export type EffectLayerNodeParams = {

View File

@ -4,7 +4,7 @@ import {
getGraphicDefinition,
registerDefaultGraphics,
} from "@/graphics";
import type { ParamValues } from "@/params";
import type { ParamValues } from "@/model/decorations/param-values";
import {
VisualNode,
type ResolvedVisualNodeState,

View File

@ -1,6 +1,6 @@
import { BaseNode } from "./base-node";
import type { TextElement } from "@/model";
import type { EffectPass } from "@/effects/types";
import type { EffectPass } from "@/model/decorations/effect";
import type { Transform } from "@/rendering";
import { drawMeasuredTextLayout } from "@/text/primitives";
import type { MeasuredTextElement } from "@/text/measure-element";

View File

@ -1,6 +1,6 @@
import { BaseNode } from "./base-node";
import type { Effect, EffectPass } from "@/effects/types";
import type { Mask } from "@/masks/types";
import type { Effect, EffectPass } from "@/model/decorations/effect";
import type { Mask } from "@/model/decorations/mask";
import type { BlendMode, Transform } from "@/rendering";
import type { RetimeConfig, VisualElement } from "@/model";

View File

@ -6,7 +6,7 @@ import {
intensityToSigma,
} from "@/effects/definitions/blur";
import { effectsRegistry, resolveEffectPasses } from "@/effects";
import type { Effect, EffectPass } from "@/effects/types";
import type { Effect, EffectPass } from "@/model/decorations/effect";
import { getSourceTimeAtClipTime } from "@/retime";
import {
DEFAULT_GRAPHIC_SOURCE_SIZE,

View File

@ -1,5 +1,5 @@
import { buildGraphicPreviewUrl, buildDefaultGraphicInstance, graphicsRegistry, registerDefaultGraphics } from "@/graphics";
import type { ParamValues } from "@/params";
import type { ParamValues } from "@/model/decorations/param-values";
import { buildStickerId, parseStickerId } from "../sticker-id";
import type {
StickerBrowseResult,

View File

@ -1,11 +1,4 @@
import type {
AnimationBindingKind,
AnimationInterpolation,
AnimationPropertyPath,
AnimationValue,
ElementAnimations,
NumericSpec,
} from "@/animation/types";
import type { AnimationBindingKind, AnimationInterpolation, AnimationPropertyPath, AnimationValue, ElementAnimations, NumericSpec } from "@/model/decorations/animations";
import { upsertPathKeyframe } from "@/animation";
import { parseColorToLinearRgba } from "@/animation/binding-values";
import { isAnimationPropertyPath } from "@/animation/path";

View File

@ -1,10 +1,4 @@
import type {
AnimationBindingKind,
AnimationInterpolation,
AnimationPath,
AnimationValue,
NumericSpec,
} from "@/animation/types";
import type { AnimationBindingKind, AnimationInterpolation, AnimationPath, AnimationValue, NumericSpec } from "@/model/decorations/animations";
import {
coerceAnimationParamValue,
getAnimationParamDefaultInterpolation,
@ -19,7 +13,7 @@ import {
} from "@/animation/graphic-param-channel";
import { effectsRegistry, registerDefaultEffects } from "@/effects";
import { getGraphicDefinition } from "@/graphics";
import type { ParamDefinition } from "@/params";
import type { ParamDefinition } from "@/model/decorations/param-values";
import type { TimelineElement } from "@/model";
import { isVisualElement } from "@/timeline/element-utils";
import { isAnimationPropertyPath } from "@/animation/path";

View File

@ -1,5 +1,5 @@
import { cloneAnimations } from "@/animation";
import type { ElementAnimations } from "@/animation/types";
import type { ElementAnimations } from "@/model/decorations/animations";
import type { MediaAsset } from "@/media/types";
import { DEFAULTS } from "@/timeline/defaults";
import type { AudioElement, CreateUploadAudioElement, TimelineElement, VideoElement } from "@/model";

View File

@ -1,7 +1,4 @@
import type {
AnimationPath,
ElementAnimations,
} from "@/animation/types";
import type { AnimationPath, ElementAnimations } from "@/model/decorations/animations";
import type { TimelineTrack } from "@/model";
import { getElementKeyframes } from "@/animation";
import { KEYFRAME_LANE_HEIGHT_PX } from "./layout";

View File

@ -3,7 +3,7 @@
import { useRef, useState, type PointerEvent } from "react";
import { useShiftKey } from "@/hooks/use-shift-key";
import { getBezierPoint } from "@/animation/bezier";
import type { NormalizedCubicBezier } from "@/animation/types";
import type { NormalizedCubicBezier } from "@/model/decorations/animations";
import { cn } from "@/utils/ui";
const GRAPH_WIDTH = 140;

View File

@ -2,7 +2,7 @@
import { useSyncExternalStore } from "react";
import { generateUUID } from "@/utils/id";
import type { NormalizedCubicBezier } from "@/animation/types";
import type { NormalizedCubicBezier } from "@/model/decorations/animations";
import type { EasingPreset } from "./easing-presets";
const STORAGE_KEY = "graph-editor-presets";

View File

@ -1,4 +1,4 @@
import type { NormalizedCubicBezier } from "@/animation/types";
import type { NormalizedCubicBezier } from "@/model/decorations/animations";
export const PRESET_MATCH_TOLERANCE = 0.02;

View File

@ -12,7 +12,7 @@ import {
PlusSignIcon,
} from "@hugeicons/core-free-icons";
import { getBezierPoint } from "@/animation/bezier";
import type { NormalizedCubicBezier } from "@/animation/types";
import type { NormalizedCubicBezier } from "@/model/decorations/animations";
import type { GraphEditorComponentOption } from "./session";
import {
BUILTIN_PRESETS,

View File

@ -6,14 +6,8 @@ import {
getScalarKeyframeContext,
updateScalarKeyframeCurve,
} from "@/animation";
import type {
AnimationPath,
ElementAnimations,
NormalizedCubicBezier,
ScalarCurveKeyframePatch,
ScalarGraphKeyframeContext,
SelectedKeyframeRef,
} from "@/animation/types";
import type { AnimationPath, ElementAnimations, NormalizedCubicBezier } from "@/model/decorations/animations";
import type { ScalarCurveKeyframePatch, ScalarGraphKeyframeContext, SelectedKeyframeRef } from "@/animation/types";
import type { SceneTracks, TimelineElement } from "@/model";
const GRAPH_LINEAR_CURVE: NormalizedCubicBezier = [0, 0, 1, 1];

View File

@ -3,7 +3,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useEditor } from "@/editor/use-editor";
import { registerCanceller } from "@/editor/cancel-interaction";
import type { NormalizedCubicBezier } from "@/animation/types";
import type { NormalizedCubicBezier } from "@/model/decorations/animations";
import { useKeyframeSelection } from "@/timeline/hooks/element/use-keyframe-selection";
import {
applyGraphEditorCurvePreview,

Some files were not shown because too many files have changed in this diff Show More