diff options
Diffstat (limited to 'apps/web/src/lib')
| -rw-r--r-- | apps/web/src/lib/actions/definitions.ts | 87 | ||||
| -rw-r--r-- | apps/web/src/lib/animation/effect-param-channel.ts | 10 | ||||
| -rw-r--r-- | apps/web/src/lib/animation/graphic-param-channel.ts | 10 | ||||
| -rw-r--r-- | apps/web/src/lib/animation/keyframe-query.ts | 2 | ||||
| -rw-r--r-- | apps/web/src/lib/animation/keyframes.ts | 4 | ||||
| -rw-r--r-- | apps/web/src/lib/animation/property-registry.ts | 10 | ||||
| -rw-r--r-- | apps/web/src/lib/animation/target-resolver.ts | 16 | ||||
| -rw-r--r-- | apps/web/src/lib/timeline/audio-state.ts | 2 | ||||
| -rw-r--r-- | apps/web/src/lib/timeline/drop-utils.ts | 15 |
9 files changed, 80 insertions, 76 deletions
diff --git a/apps/web/src/lib/actions/definitions.ts b/apps/web/src/lib/actions/definitions.ts index 4d67c1ef..f6bdd65c 100644 --- a/apps/web/src/lib/actions/definitions.ts +++ b/apps/web/src/lib/actions/definitions.ts @@ -1,4 +1,8 @@ -import type { ShortcutKey } from "@/lib/actions/keybinding"; +import type { + KeybindingConfig, + ShortcutKey, +} from "@/lib/actions/keybinding"; +import type { TActionWithOptionalArgs } from "./types"; export type TActionCategory = | "playback" @@ -10,18 +14,20 @@ export type TActionCategory = | "controls" | "assets"; -export interface TActionDefinition { +export interface TActionBaseDefinition { description: string; category: TActionCategory; - defaultShortcuts?: ShortcutKey[]; args?: Record<string, unknown>; } +export interface TActionDefinition extends TActionBaseDefinition { + defaultShortcuts?: readonly ShortcutKey[]; +} + export const ACTIONS = { "toggle-play": { description: "Play/Pause", category: "playback", - defaultShortcuts: ["space", "k"], }, "stop-playback": { description: "Stop playback", @@ -30,81 +36,66 @@ export const ACTIONS = { "seek-forward": { description: "Seek forward 1 second", category: "playback", - defaultShortcuts: ["l"], args: { seconds: "number" }, }, "seek-backward": { description: "Seek backward 1 second", category: "playback", - defaultShortcuts: ["j"], args: { seconds: "number" }, }, "frame-step-forward": { description: "Frame step forward", category: "navigation", - defaultShortcuts: ["right"], }, "frame-step-backward": { description: "Frame step backward", category: "navigation", - defaultShortcuts: ["left"], }, "jump-forward": { description: "Jump forward 5 seconds", category: "navigation", - defaultShortcuts: ["shift+right"], args: { seconds: "number" }, }, "jump-backward": { description: "Jump backward 5 seconds", category: "navigation", - defaultShortcuts: ["shift+left"], args: { seconds: "number" }, }, "goto-start": { description: "Go to timeline start", category: "navigation", - defaultShortcuts: ["home", "enter"], }, "goto-end": { description: "Go to timeline end", category: "navigation", - defaultShortcuts: ["end"], }, split: { description: "Split elements at playhead", category: "editing", - defaultShortcuts: ["s"], }, "split-left": { description: "Split and remove left", category: "editing", - defaultShortcuts: ["q"], }, "split-right": { description: "Split and remove right", category: "editing", - defaultShortcuts: ["w"], }, "delete-selected": { description: "Delete selected elements", category: "editing", - defaultShortcuts: ["backspace", "delete"], }, "copy-selected": { description: "Copy selected elements", category: "editing", - defaultShortcuts: ["ctrl+c"], }, "paste-copied": { description: "Paste elements at playhead", category: "editing", - defaultShortcuts: ["ctrl+v"], }, "toggle-snapping": { description: "Toggle snapping", category: "editing", - defaultShortcuts: ["n"], }, "toggle-ripple-editing": { description: "Toggle ripple editing", @@ -113,12 +104,10 @@ export const ACTIONS = { "select-all": { description: "Select all elements", category: "selection", - defaultShortcuts: ["ctrl+a"], }, "cancel-interaction": { description: "Cancel current interaction", category: "controls", - defaultShortcuts: ["escape"], }, "deselect-all": { description: "Deselect all elements", @@ -127,7 +116,6 @@ export const ACTIONS = { "duplicate-selected": { description: "Duplicate selected element", category: "selection", - defaultShortcuts: ["ctrl+d"], }, "toggle-elements-muted-selected": { description: "Mute/unmute selected elements", @@ -144,12 +132,10 @@ export const ACTIONS = { undo: { description: "Undo", category: "history", - defaultShortcuts: ["ctrl+z"], }, redo: { description: "Redo", category: "history", - defaultShortcuts: ["ctrl+shift+z", "ctrl+y"], }, "remove-media-asset": { description: "Remove media asset", @@ -161,30 +147,59 @@ export const ACTIONS = { category: "assets", args: { projectId: "string", assetIds: "string[]" }, }, -} as const satisfies Record<string, TActionDefinition>; +} as const satisfies Record<string, TActionBaseDefinition>; export type TAction = keyof typeof ACTIONS; +const ACTION_DEFAULT_SHORTCUTS = { + "toggle-play": ["space", "k"], + "seek-forward": ["l"], + "seek-backward": ["j"], + "frame-step-forward": ["right"], + "frame-step-backward": ["left"], + "jump-forward": ["shift+right"], + "jump-backward": ["shift+left"], + "goto-start": ["home", "enter"], + "goto-end": ["end"], + split: ["s"], + "split-left": ["q"], + "split-right": ["w"], + "delete-selected": ["backspace", "delete"], + "copy-selected": ["ctrl+c"], + "paste-copied": ["ctrl+v"], + "toggle-snapping": ["n"], + "select-all": ["ctrl+a"], + "cancel-interaction": ["escape"], + "duplicate-selected": ["ctrl+d"], + undo: ["ctrl+z"], + redo: ["ctrl+shift+z", "ctrl+y"], +} as const satisfies Partial<Record<TActionWithOptionalArgs, readonly ShortcutKey[]>>; + +const ACTION_DEFAULT_SHORTCUTS_BY_ACTION: Partial< + Record<TAction, readonly ShortcutKey[]> +> = ACTION_DEFAULT_SHORTCUTS; + export function getActionDefinition({ action, }: { action: TAction; }): TActionDefinition { - return ACTIONS[action]; + return { + ...ACTIONS[action], + defaultShortcuts: ACTION_DEFAULT_SHORTCUTS_BY_ACTION[action], + }; } -export function getDefaultShortcuts(): Record<ShortcutKey, TAction> { - const shortcuts: Record<string, TAction> = {}; +export function getDefaultShortcuts(): KeybindingConfig { + const shortcuts: KeybindingConfig = {}; - for (const [action, def] of Object.entries(ACTIONS) as Array< - [TAction, TActionDefinition] - >) { - if (def.defaultShortcuts) { - for (const shortcut of def.defaultShortcuts) { - shortcuts[shortcut] = action; - } + for (const [action, defaultShortcuts] of Object.entries( + ACTION_DEFAULT_SHORTCUTS, + ) as Array<[TActionWithOptionalArgs, readonly ShortcutKey[]]>) { + for (const shortcut of defaultShortcuts) { + shortcuts[shortcut] = action; } } - return shortcuts as Record<ShortcutKey, TAction>; + return shortcuts; } diff --git a/apps/web/src/lib/animation/effect-param-channel.ts b/apps/web/src/lib/animation/effect-param-channel.ts index 323d9f54..a614c14f 100644 --- a/apps/web/src/lib/animation/effect-param-channel.ts +++ b/apps/web/src/lib/animation/effect-param-channel.ts @@ -26,11 +26,9 @@ export function buildEffectParamPath({ return `${EFFECT_PARAM_PATH_PREFIX}${effectId}${EFFECT_PARAM_PATH_SUFFIX}${paramKey}`; } -export function isEffectParamPath({ - propertyPath, -}: { - propertyPath: string; -}): propertyPath is EffectParamPath { +export function isEffectParamPath( + propertyPath: string, +): propertyPath is EffectParamPath { return ( propertyPath.startsWith(EFFECT_PARAM_PATH_PREFIX) && propertyPath.includes(EFFECT_PARAM_PATH_SUFFIX) @@ -42,7 +40,7 @@ export function parseEffectParamPath({ }: { propertyPath: string; }): { effectId: string; paramKey: string } | null { - if (!isEffectParamPath({ propertyPath })) { + if (!isEffectParamPath(propertyPath)) { return null; } diff --git a/apps/web/src/lib/animation/graphic-param-channel.ts b/apps/web/src/lib/animation/graphic-param-channel.ts index e77e4c73..7db544e3 100644 --- a/apps/web/src/lib/animation/graphic-param-channel.ts +++ b/apps/web/src/lib/animation/graphic-param-channel.ts @@ -20,11 +20,9 @@ export function buildGraphicParamPath({ return `${GRAPHIC_PARAM_PATH_PREFIX}${paramKey}`; } -export function isGraphicParamPath({ - propertyPath, -}: { - propertyPath: string; -}): propertyPath is GraphicParamPath { +export function isGraphicParamPath( + propertyPath: string, +): propertyPath is GraphicParamPath { return propertyPath.startsWith(GRAPHIC_PARAM_PATH_PREFIX); } @@ -33,7 +31,7 @@ export function parseGraphicParamPath({ }: { propertyPath: string; }): { paramKey: string } | null { - if (!isGraphicParamPath({ propertyPath })) { + if (!isGraphicParamPath(propertyPath)) { return null; } diff --git a/apps/web/src/lib/animation/keyframe-query.ts b/apps/web/src/lib/animation/keyframe-query.ts index 753e31d0..637b0289 100644 --- a/apps/web/src/lib/animation/keyframe-query.ts +++ b/apps/web/src/lib/animation/keyframe-query.ts @@ -20,7 +20,7 @@ export function getElementKeyframes({ if ( !channel || channel.keyframes.length === 0 || - !isAnimationPath({ propertyPath }) + !isAnimationPath(propertyPath) ) { return []; } diff --git a/apps/web/src/lib/animation/keyframes.ts b/apps/web/src/lib/animation/keyframes.ts index 5fa99fb8..13cdf238 100644 --- a/apps/web/src/lib/animation/keyframes.ts +++ b/apps/web/src/lib/animation/keyframes.ts @@ -616,8 +616,8 @@ export function splitAnimationsAtTime({ time: splitTime, fallbackValue: normalizedChannel.keyframes[0].value, }); - const knownPropertyPath = isAnimationPropertyPath({ propertyPath }) - ? (propertyPath as AnimationPropertyPath) + const knownPropertyPath = isAnimationPropertyPath(propertyPath) + ? propertyPath : null; const boundaryInterpolation = knownPropertyPath ? getDefaultInterpolationForProperty({ diff --git a/apps/web/src/lib/animation/property-registry.ts b/apps/web/src/lib/animation/property-registry.ts index 0b28e783..46990754 100644 --- a/apps/web/src/lib/animation/property-registry.ts +++ b/apps/web/src/lib/animation/property-registry.ts @@ -245,12 +245,10 @@ const ANIMATION_PROPERTY_REGISTRY: Record< }, }; -export function isAnimationPropertyPath({ - propertyPath, -}: { - propertyPath: string; -}): boolean { - return propertyPath in ANIMATION_PROPERTY_REGISTRY; +export function isAnimationPropertyPath( + propertyPath: string, +): propertyPath is AnimationPropertyPath { + return Object.hasOwn(ANIMATION_PROPERTY_REGISTRY, propertyPath); } export function getAnimationPropertyDefinition({ diff --git a/apps/web/src/lib/animation/target-resolver.ts b/apps/web/src/lib/animation/target-resolver.ts index e801bc68..10b83775 100644 --- a/apps/web/src/lib/animation/target-resolver.ts +++ b/apps/web/src/lib/animation/target-resolver.ts @@ -204,15 +204,13 @@ function buildEffectParamDescriptor({ }; } -export function isAnimationPath({ - propertyPath, -}: { - propertyPath: string; -}): propertyPath is AnimationPath { +export function isAnimationPath( + propertyPath: string, +): propertyPath is AnimationPath { return ( - isAnimationPropertyPath({ propertyPath }) || - isGraphicParamPath({ propertyPath }) || - isEffectParamPath({ propertyPath }) + isAnimationPropertyPath(propertyPath) || + isGraphicParamPath(propertyPath) || + isEffectParamPath(propertyPath) ); } @@ -223,7 +221,7 @@ export function resolveAnimationTarget({ element: TimelineElement; path: AnimationPath; }): AnimationPathDescriptor | null { - if (isAnimationPropertyPath({ propertyPath: path })) { + if (isAnimationPropertyPath(path)) { const propertyDefinition = getAnimationPropertyDefinition({ propertyPath: path, }); diff --git a/apps/web/src/lib/timeline/audio-state.ts b/apps/web/src/lib/timeline/audio-state.ts index 90d34668..4bc3a7e1 100644 --- a/apps/web/src/lib/timeline/audio-state.ts +++ b/apps/web/src/lib/timeline/audio-state.ts @@ -18,7 +18,7 @@ export function clampDb(value: number): number { } export function dBToLinear(db: number): number { - return Math.pow(10, clampDb(db) / 20); + return 10 ** (clampDb(db) / 20); } export function hasAnimatedVolume({ diff --git a/apps/web/src/lib/timeline/drop-utils.ts b/apps/web/src/lib/timeline/drop-utils.ts index b4f53948..d8206474 100644 --- a/apps/web/src/lib/timeline/drop-utils.ts +++ b/apps/web/src/lib/timeline/drop-utils.ts @@ -6,7 +6,11 @@ import type { import { TRACK_CONFIG, TRACK_GAP } from "@/constants/timeline-constants"; import { wouldElementOverlap } from "./element-utils"; import type { ComputeDropTargetParams, DropTarget } from "@/lib/timeline"; -import { isMainTrack, enforceMainTrackStart } from "./track-utils"; +import { + canElementGoOnTrack, + isMainTrack, + enforceMainTrackStart, +} from "./track-utils"; function findElementAtPosition({ mouseX, @@ -85,14 +89,7 @@ function isCompatible({ elementType: ElementType; trackType: TimelineTrack["type"]; }): boolean { - if (elementType === "text") return trackType === "text"; - if (elementType === "audio") return trackType === "audio"; - if (elementType === "sticker") return trackType === "sticker"; - if (elementType === "effect") return trackType === "effect"; - if (elementType === "video" || elementType === "image") { - return trackType === "video"; - } - return false; + return canElementGoOnTrack({ elementType, trackType }); } function getMainTrackIndex({ tracks }: { tracks: TimelineTrack[] }): number { |
