diff --git a/apps/web/src/actions/keybinding.ts b/apps/web/src/actions/keybinding.ts index aeec2fd6d..fc9a17ae2 100644 --- a/apps/web/src/actions/keybinding.ts +++ b/apps/web/src/actions/keybinding.ts @@ -38,6 +38,22 @@ export type SingleCharacterShortcutKey = `${Key}`; export type ShortcutKey = ModifierBasedShortcutKey | SingleCharacterShortcutKey; +const MODIFIER_KEYS_SET: ReadonlySet = new Set([ + "ctrl", "alt", "shift", + "ctrl+shift", "alt+shift", "ctrl+alt", "ctrl+alt+shift", +]); + +export function isShortcutKey(value: string): value is ShortcutKey { + // Single key (e.g. "a", "enter") + if (isKey(value)) return true; + // Modifier+key (e.g. "ctrl+s", "ctrl+shift+z") + const lastPlus = value.lastIndexOf("+"); + if (lastPlus === -1) return false; + const modifier = value.slice(0, lastPlus); + const key = value.slice(lastPlus + 1); + return MODIFIER_KEYS_SET.has(modifier) && isKey(key); +} + export type KeybindingConfig = { [key in ShortcutKey]?: TActionWithOptionalArgs; }; diff --git a/apps/web/src/actions/types.ts b/apps/web/src/actions/types.ts index 38627b957..9a59f8717 100644 --- a/apps/web/src/actions/types.ts +++ b/apps/web/src/actions/types.ts @@ -1,5 +1,6 @@ import type { MutableRefObject } from "react"; import type { TAction } from "./definitions"; +import { ACTIONS } from "./definitions"; export type { TAction }; @@ -24,6 +25,23 @@ export type TActionWithOptionalArgs = export type TActionWithNoArgs = Exclude; +const ACTION_KEYS_SET: ReadonlySet = new Set(Object.keys(ACTIONS)); + +const ACTIONS_WITH_REQUIRED_ARGS: ReadonlySet = new Set([ + "remove-media-asset", + "remove-media-assets", +]); + +export function isAction(value: string): value is TAction { + return ACTION_KEYS_SET.has(value); +} + +export function isActionWithOptionalArgs(value: string): value is TActionWithOptionalArgs { + if (!isAction(value)) return false; + // Actions that require mandatory (non-undefined) args cannot be used as keybindings + return !ACTIONS_WITH_REQUIRED_ARGS.has(value); +} + export type TArgOfAction = A extends TActionWithArgs ? TActionArgsMap[A] : undefined; diff --git a/apps/web/src/services/storage/migrations/runner.ts b/apps/web/src/services/storage/migrations/runner.ts index eed8071f8..162db8ca9 100644 --- a/apps/web/src/services/storage/migrations/runner.ts +++ b/apps/web/src/services/storage/migrations/runner.ts @@ -38,11 +38,11 @@ export async function runStorageMigrations({ hasCleanedUpMetaDb = true; } - const projectsAdapter = new IndexedDBAdapter( - "video-editor-projects", - "projects", - 1, - ); + const projectsAdapter = new IndexedDBAdapter({ + dbName: "video-editor-projects", + storeName: "projects", + version: 1, + }); const projects = await projectsAdapter.getAll(); @@ -95,7 +95,7 @@ export async function runStorageMigrations({ break; } - await projectsAdapter.set(projectId, result.project); + await projectsAdapter.set({ key: projectId, value: result.project }); migratedCount++; currentVersion = migration.to; projectRecord = result.project; diff --git a/apps/web/src/services/storage/migrations/v1-to-v2.ts b/apps/web/src/services/storage/migrations/v1-to-v2.ts index a87b4a86f..601e47975 100644 --- a/apps/web/src/services/storage/migrations/v1-to-v2.ts +++ b/apps/web/src/services/storage/migrations/v1-to-v2.ts @@ -121,20 +121,20 @@ async function loadLegacyTracksForScene({ const sceneDbName = `video-editor-timelines-${projectId}-${sceneId}`; const projectDbName = `video-editor-timelines-${projectId}`; - const adapter = new IndexedDBAdapter( - sceneDbName, - "timeline", - 1, - ); + const adapter = new IndexedDBAdapter({ + dbName: sceneDbName, + storeName: "timeline", + version: 1, + }); let data = await adapter.get("timeline"); if (!data && isMain) { - const projectAdapter = new IndexedDBAdapter( - projectDbName, - "timeline", - 1, - ); + const projectAdapter = new IndexedDBAdapter({ + dbName: projectDbName, + storeName: "timeline", + version: 1, + }); data = await projectAdapter.get("timeline"); } @@ -157,11 +157,11 @@ async function loadMediaTypesById({ return {}; } - const mediaMetadataAdapter = new IndexedDBAdapter( - `video-editor-media-${projectId}`, - "media-metadata", - 1, - ); + const mediaMetadataAdapter = new IndexedDBAdapter({ + dbName: `video-editor-media-${projectId}`, + storeName: "media-metadata", + version: 1, + }); const mediaEntries = await Promise.all( mediaIds.map(async (mediaId) => { diff --git a/apps/web/src/stickers/providers/index.ts b/apps/web/src/stickers/providers/index.ts index 56b3f6cb6..b0ea6efc1 100644 --- a/apps/web/src/stickers/providers/index.ts +++ b/apps/web/src/stickers/providers/index.ts @@ -19,6 +19,6 @@ export function registerDefaultStickerProviders({ if (stickersRegistry.has(provider.id)) { continue; } - stickersRegistry.register(provider.id, provider); + stickersRegistry.register({ key: provider.id, definition: provider }); } }