Skip to content

Function activationKey cannot reliably support Ctrl+C on macOS #525

Description

@slonweiss

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

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions