codebase overhaul (#697)

This commit is contained in:
Maze
2026-01-31 00:20:04 +01:00
committed by GitHub
parent 0173db9944
commit 7bf0984698
469 changed files with 36184 additions and 32931 deletions
+162
View File
@@ -0,0 +1,162 @@
import type { ShortcutKey } from "@/types/keybinding";
export type TActionCategory =
| "playback"
| "navigation"
| "editing"
| "selection"
| "history"
| "timeline"
| "controls";
export interface TActionDefinition {
description: string;
category: TActionCategory;
defaultShortcuts?: ShortcutKey[];
args?: Record<string, unknown>;
}
export const ACTIONS = {
"toggle-play": {
description: "Play/Pause",
category: "playback",
defaultShortcuts: ["space", "k"],
},
"stop-playback": {
description: "Stop playback",
category: "playback",
},
"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"],
},
"select-all": {
description: "Select all elements",
category: "selection",
defaultShortcuts: ["ctrl+a"],
},
"duplicate-selected": {
description: "Duplicate selected element",
category: "selection",
defaultShortcuts: ["ctrl+d"],
},
"toggle-elements-muted-selected": {
description: "Mute/unmute selected elements",
category: "selection",
},
"toggle-elements-visibility-selected": {
description: "Show/hide selected elements",
category: "selection",
},
"toggle-bookmark": {
description: "Toggle bookmark at playhead",
category: "timeline",
},
undo: {
description: "Undo",
category: "history",
defaultShortcuts: ["ctrl+z"],
},
redo: {
description: "Redo",
category: "history",
defaultShortcuts: ["ctrl+shift+z", "ctrl+y"],
},
} as const satisfies Record<string, TActionDefinition>;
export type TAction = keyof typeof ACTIONS;
export function getActionDefinition(action: TAction): TActionDefinition {
return ACTIONS[action];
}
export function getDefaultShortcuts(): Record<ShortcutKey, TAction> {
const shortcuts: Record<string, TAction> = {};
for (const [action, def] of Object.entries(ACTIONS) as Array<
[TAction, TActionDefinition]
>) {
if (def.defaultShortcuts) {
for (const shortcut of def.defaultShortcuts) {
shortcuts[shortcut] = action;
}
}
}
return shortcuts as Record<ShortcutKey, TAction>;
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./definitions";
export * from "./types";
export * from "./registry";
+61
View File
@@ -0,0 +1,61 @@
import type {
TAction,
TActionFunc,
TActionWithArgs,
TActionWithOptionalArgs,
TActionArgsMap,
TArgOfAction,
TInvocationTrigger,
} from "./types";
type ActionHandler = (arg: unknown, trigger?: TInvocationTrigger) => void;
const boundActions: Partial<Record<TAction, ActionHandler[]>> = {};
export function bindAction<A extends TAction>(
action: A,
handler: TActionFunc<A>,
) {
const handlers = boundActions[action];
const typedHandler = handler as ActionHandler;
if (handlers) {
handlers.push(typedHandler);
} else {
boundActions[action] = [typedHandler];
}
}
export function unbindAction<A extends TAction>(
action: A,
handler: TActionFunc<A>,
) {
const handlers = boundActions[action];
if (!handlers) return;
const typedHandler = handler as ActionHandler;
boundActions[action] = handlers.filter((h) => h !== typedHandler);
if (boundActions[action]?.length === 0) {
delete boundActions[action];
}
}
type InvokeActionFunc = {
(
action: TActionWithOptionalArgs,
args?: undefined,
trigger?: TInvocationTrigger,
): void;
<A extends TActionWithArgs>(
action: A,
args: TActionArgsMap[A],
trigger?: TInvocationTrigger,
): void;
};
export const invokeAction: InvokeActionFunc = <A extends TAction>(
action: A,
args?: TArgOfAction<A>,
trigger?: TInvocationTrigger,
) => {
boundActions[action]?.forEach((handler) => handler(args, trigger));
};
+42
View File
@@ -0,0 +1,42 @@
import type { MutableRefObject } from "react";
import type { TAction } from "./definitions";
export type { TAction };
export type TActionArgsMap = {
"seek-forward": { seconds: number } | undefined;
"seek-backward": { seconds: number } | undefined;
"jump-forward": { seconds: number } | undefined;
"jump-backward": { seconds: number } | undefined;
};
type TKeysWithValueUndefined<T> = {
[K in keyof T]: undefined extends T[K] ? K : never;
}[keyof T];
export type TActionWithArgs = keyof TActionArgsMap;
export type TActionWithOptionalArgs =
| TActionWithNoArgs
| TKeysWithValueUndefined<TActionArgsMap>;
export type TActionWithNoArgs = Exclude<TAction, TActionWithArgs>;
export type TArgOfAction<A extends TAction> = A extends TActionWithArgs
? TActionArgsMap[A]
: undefined;
export type TActionFunc<A extends TAction> = A extends TActionWithArgs
? (arg: TArgOfAction<A>, trigger?: TInvocationTrigger) => void
: (_?: undefined, trigger?: TInvocationTrigger) => void;
export type TInvocationTrigger = "keypress" | "mouseclick";
export type TBoundActionList = {
[A in TAction]?: Array<TActionFunc<A>>;
};
export type TActionHandlerOptions =
| MutableRefObject<boolean>
| boolean
| undefined;