Function activationKey cannot reliably support Ctrl+C on macOS

Author: slonweissCreated Jul 4, 2026Updated Jul 4, 2026

Summary

I am trying to configure React Grab so either Cmd+C or Ctrl+C can trigger the copy/grab shortcut on macOS.

Using a function activationKey lets the keydown path recognize both shortcuts, but Ctrl+C still does not reliably activate/copy. It looks like the keyup/release path derives required modifiers from the platform default when activationKey is a function, so on macOS it still treats Meta as the required modifier even when the custom matcher matched Ctrl+C.

Example configuration

typescript
import { getGlobalApi } from "react-grab";

const reactGrab = getGlobalApi();

reactGrab?.setOptions({
  activationKey: (event) => {
    const key = event.key.toLowerCase();
    const isCopyKey = key === "c" || event.code === "KeyC";
    return isCopyKey && (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
  },
  keyHoldDuration: 0,
});

Expected behavior

When activationKey is a function and returns true for Ctrl+C, React Grab should use that matched shortcut consistently for the full hold/copy lifecycle.

In this case, both Cmd+C and Ctrl+C should be viable on macOS.

Actual behavior

Cmd+C behaves as expected on macOS, but Ctrl+C does not reliably trigger/copy, even when activationKey returns true and keyHoldDuration is set to 0.

Source-level notes

From reading the current source:

  • packages/react-grab/src/utils/is-target-key-combination.ts honors a function activationKey in the keydown path.
  • packages/react-grab/src/utils/parse-activation-key.ts has getModifiersFromActivationKey(), but for a function activation key it returns platform defaults:
    • macOS: metaKey: true
    • non-macOS: ctrlKey: true
  • packages/react-grab/src/core/index.tsx uses getRequiredModifiers() in the keyup path to compute isReleasingModifier.

That means a function matcher can say "this Ctrl+C keydown is valid", but the later release logic still appears to evaluate macOS Meta as the required modifier. For an OR-style shortcut like Cmd+C or Ctrl+C, there does not seem to be a way for the function matcher to also tell React Grab which modifier/key combination was actually matched.

There is also a MIN_HOLD_FOR_ACTIVATION_AFTER_COPY_MS gate in the keyup path, so setting keyHoldDuration: 0 is not enough to make a normal Ctrl+C press work reliably.

Why this matters

The string form can represent one shortcut such as "ctrl+c", but it cannot represent an OR shortcut like "use Cmd+C or Ctrl+C". The function form is the natural API for that, but today it loses release/modifier semantics.

Possible fixes

A few possible directions:

  • Let activationKey accept an array of strings/functions, e.g. ["cmd+c", "ctrl+c"], so modifier metadata remains parseable.
  • Add an optional way for function matchers to declare required modifiers.
  • Track the shortcut/modifiers that actually matched on keydown and use that for keyup/release handling instead of falling back to platform defaults for function activation keys.

Related but not the same as #398, which is about copy behavior while the toolbar is minimized. This issue is specifically about function activationKey release semantics on macOS.