Video Studio SDKv0.0.3
Plugin authoringExtension points

Hotkey

Keyboard shortcut registration — scopes, modifiers, and how they interact with built-ins.

A hotkey registers a keyboard shortcut. The SDK calls your onTrigger when the user presses the key combination — scoped to a specific focus zone or "always" (works anywhere in the editor).

Registration shape

interface HotkeyRegistration {
  id:         string
  key:        string                                     // 'e', 'd', 'ArrowLeft' — see notes below
  modifiers?: ('ctrl' | 'shift' | 'alt' | 'meta')[]
  label:      string                                     // shown in shortcut listings
  onTrigger:  () => void
  when?:      'always' | 'timeline-focused' | 'player-focused'
}

Registering one

ctx.registerHotkey({
  id:        'mycompany:apply-effect',
  key:       'e',
  modifiers: ['ctrl'],
  label:     'Apply effect',
  when:      'always',
  onTrigger: () => {
    const state = ctx.store.getState()
    // Apply effect to selected items, for example
    for (const id of state.selection.selectedItemIds) {
      state.updateItemProperties(id, { /* … */ })
    }
  },
})

Field reference

id

Unique. The registry de-duplicates by id.

key

The KeyboardEvent code or key (the SDK matches on lowercased input). Single letters work as expected ('e', 's'). For named keys use the standard names: 'ArrowLeft', 'Escape', 'Space', 'Enter'.

modifiers

Combination of 'ctrl', 'shift', 'alt', 'meta'. On macOS, meta is Cmd. The SDK uses ctrl || meta interchangeably for cross-platform shortcuts — pass ['ctrl'] and it triggers on both Cmd+key (Mac) and Ctrl+key (Windows).

label

Human-readable description. Shown in any shortcut-listing UI the host app builds (the SDK doesn't ship one by default).

when

Scope — when the hotkey is active:

ValueFires when
'always' (default)The user is anywhere in the editor and not typing in an input
'timeline-focused'The timeline panel has focus
'player-focused'The player has focus

All three scopes ignore inputs, textareas, selects, and contenteditable — the user never accidentally triggers a hotkey while typing.

onTrigger

The handler. Same conventions as toolbar actions: read state via ctx.store.getState(), dispatch via store actions, batch multi-step changes.

Avoid collisions with built-ins

The SDK reserves a set of hotkeys via useHotkeys():

ShortcutBuilt-in action
SpacePlay / pause toggle
SSplit selected item at playhead
Delete / BackspaceRemove selected items
⌘D / Ctrl+DDuplicate selected items
⌘Z / Ctrl+ZUndo
⌘⇧Z / Ctrl+Shift+ZRedo
⌘A / Ctrl+ASelect all
EscapeDeselect all
/ Nudge selection by 1 frame (or seek if no selection)
Shift+← / Shift+→Nudge / seek by 10 frames
+ / -Zoom in / out
Home / EndJump to start / end

Don't register hotkeys that collide with these — the user will get unpredictable behavior. Pick something free (Shift+letter, Alt+letter, or a less-loaded modifier combo).

Patterns

Repeat-friendly action

Hotkeys repeat by default while the key is held. For actions you don't want repeating (e.g. "add marker"), guard with a flag:

let armed = true
ctx.registerHotkey({
  id: 'mycompany:add-marker',
  key: 'm',
  label: 'Add marker',
  when: 'always',
  onTrigger: () => {
    if (!armed) return
    armed = false
    const state = ctx.store.getState()
    state.addMarker(state.player.currentFrame)
    requestAnimationFrame(() => { armed = true })
  },
})

A cleaner approach is to listen for keyup yourself — but that requires bypassing the registration helper, which most plugins don't need.

Shortcut that mirrors a toolbar action

If you register the same logic as both a toolbar action and a hotkey, factor the implementation out:

function applyEffect(store: StudioStore) { /* … */ }

ctx.registerToolbarAction({
  id: 'mycompany:apply-effect',
  label: 'Apply effect',
  icon: ctx.icons.sliders,
  order: 50,
  onClick: () => applyEffect(ctx.store),
})

ctx.registerHotkey({
  id: 'mycompany:apply-effect-shortcut',
  key: 'e',
  modifiers: ['ctrl'],
  label: 'Apply effect',
  when: 'always',
  onTrigger: () => applyEffect(ctx.store),
})

// You can also use it as the displayed shortcut on the context-menu entry:
ctx.registerContextMenuAction({
  id: 'mycompany:apply-effect-menu',
  label: 'Apply effect',
  shortcut: '⌘E',
  order: 50,
  onAction: () => applyEffect(ctx.store),
})

The user can discover the action through any of the three surfaces; the implementation lives in one place.

On this page