Repository: motion-canvas/motion-canvas
File: packages/ui/src/contexts/shortcuts.tsx (TypeScript)

Code before:

export type ShortcutModules = 'global' | 'timeline' | 'viewport' | 'none';
type ShortcutsByModule = Record<ShortcutModules, Shortcut[]>;
type ShortcutsState = {
  currentModule: ShortcutModules;
  shortcuts: ShortcutsByModule;
  setCurrentModule?: (module: ShortcutModules) => void;
};

const InitialShortcuts: ShortcutsByModule = {
  global: [
    {key: 'Space', action: 'Toggle playback'},
    {key: '<-', action: 'Previous frame'},
    {key: '->', action: 'Next frame'},
    {key: 'Shift + <-', action: 'Reset to first frame'},
    {key: 'Shift + ->', action: 'Seek to last frame'},
    {key: 'M', action: 'Toggle audio'},
    {key: 'L', action: 'Toggle loop'},
  ],
  viewport: [
    {key: '0', action: 'Zoom to fit'},
    {key: '=', action: 'Zoom in'},
    {key: '-', action: 'Zoom out'},
    {key: "'", action: 'Toggle grid'},
    {key: 'v', action: 'Toggle coordinates'},
    {key: 'c', action: 'Copy coordinates'},
    {
      key: 'I',
      action: 'Use color picker',
      available: () => typeof EyeDropper === 'function',
    },
  ],
  timeline: [{key: 'F', action: 'Focus playhead'}],
  none: [],
};

const ShortcutsContext = createContext<ShortcutsState>({
  currentModule: 'none',
  shortcuts: InitialShortcuts,
});

export function ShortcutsProvider({children}: {children: ComponentChildren}) {
  const [currentModule, setCurrentModule] = useState<ShortcutModules>('none');

  return (
    <ShortcutsContext.Provider
      value={{currentModule, setCurrentModule, shortcuts: InitialShortcuts}}
    >
      {children}
    </ShortcutsContext.Provider>
  );

Review comment (suggestion):
I'm not a huge fan of adding multiple shortcuts to do one thing.
I'd imagine it working the same way as the color picker, so:
1. **Initial State** - the coordinates are displayed in the top left, next to a button
    a. Clicking on the button switches to the **Sampling State**
    b. Pressing `v` switches to the **Sampling State**
3. **Sampling State**
    a. Clicking LMB anywhere on the canvas copies the position.
    b. Clicking ESC cancels sampling.

Code after:

export type ShortcutModules = 'global' | 'timeline' | 'viewport' | 'none';
type ShortcutsByModule = Record<ShortcutModules, Shortcut[]>;
type ShortcutsState = {
  currentModule: ShortcutModules;
  shortcuts: ShortcutsByModule;
  setCurrentModule?: (module: ShortcutModules) => void;
};

const InitialShortcuts: ShortcutsByModule = {
  global: [
    {key: 'Space', action: 'Toggle playback'},
    {key: '<-', action: 'Previous frame'},
    {key: '->', action: 'Next frame'},
    {key: 'Shift + <-', action: 'Reset to first frame'},
    {key: 'Shift + ->', action: 'Seek to last frame'},
    {key: 'M', action: 'Toggle audio'},
    {key: 'L', action: 'Toggle loop'},
  ],
  viewport: [
    {key: '0', action: 'Zoom to fit'},
    {key: '=', action: 'Zoom in'},
    {key: '-', action: 'Zoom out'},
    {key: "'", action: 'Toggle grid'},
    {key: 'P', action: 'Copy coordinates'},
    {
      key: 'I',
      action: 'Use color picker',
      available: () => typeof EyeDropper === 'function',
    },
  ],
  timeline: [{key: 'F', action: 'Focus playhead'}],
  none: [],
};

const ShortcutsContext = createContext<ShortcutsState>({
  currentModule: 'none',
  shortcuts: InitialShortcuts,
});

export function ShortcutsProvider({children}: {children: ComponentChildren}) {
  const [currentModule, setCurrentModule] = useState<ShortcutModules>('none');

  return (
    <ShortcutsContext.Provider
      value={{currentModule, setCurrentModule, shortcuts: InitialShortcuts}}
    >
      {children}
    </ShortcutsContext.Provider>
  );
}