feat(release): v2.0.0 - cascade mode, priority scopes, hooks, zero-blinking react architecture, and ci/cd workflows - #1
Conversation
blinking react architecture, and ci/cd workflows
There was a problem hiding this comment.
Pull request overview
This PR prepares the v2.0.0 release of Keybindy by introducing cascading scope mode with priority weights, input-aware shortcut suppression, and before/after shortcut hooks in @keybindy/core, while modernizing @keybindy/react around useShortcut / useShortcuts and updating docs, samples, and CI/CD workflows.
Changes:
- Add cascade scope mode + numeric scope priorities, input target handling (
ignoreInputs/enableInInput), andbeforeEach/afterEachhooks to@keybindy/core. - Introduce
useShortcut/useShortcutsand expand programmatic manager access in@keybindy/react; removeShortcutLabel; update tests and docs. - Bump package versions to 2.0.0, update changelogs/READMEs, and add CI + release workflows with Changesets config updates.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/sample-react/src/App.jsx | Updates the React sample to exercise new scoping/priority/input behavior. |
| tests/sample-project/src/main.js | Extends vanilla sample registrations for sequential shortcuts, cascade mode, priority, and input handling. |
| tests/sample-project/index.html | Adds input elements to test input-ignoring behavior. |
| README.md | Refreshes top-level project documentation and quick examples. |
| packages/react/src/useShortcuts.ts | Adds new React hooks (useShortcuts, useShortcut) implementing stable handler refs and lifecycle behavior. |
| packages/react/src/useKeybindy.tsx | Expands manager hook surface (scope mode/priority/hooks) and adds deprecation aliasing. |
| packages/react/src/types.ts | Updates React shortcut typing to use the new ShortcutBinding type. |
| packages/react/src/ShortcutLabel.tsx | Removes the UI helper component to keep the React package headless. |
| packages/react/src/Keybindy.tsx | Refactors <Keybindy /> to delegate registration logic to useShortcuts. |
| packages/react/src/Keybindy.test.tsx | Updates/extends React unit tests for new APIs and behaviors. |
| packages/react/src/index.ts | Re-exports new hooks/types and expands core type exports. |
| packages/react/README.md | Rewrites React package docs to document the new hooks, scoping, and hooks APIs. |
| packages/react/package.json | Bumps @keybindy/react to 2.0.0 and adjusts export typings. |
| packages/react/CHANGELOG.md | Adds v2.0.0 changelog entry describing major React changes. |
| packages/core/src/utils/isInputTarget.ts | Adds helper for detecting input/textarea/select/contenteditable targets. |
| packages/core/src/utils/expandAliases.ts | Extends key alias coverage (cmd/command/option/esc/return, etc.). |
| packages/core/src/types.ts | Adds new public types (ScopeMode, Key, ShortcutBinding, hooks, manager options). |
| packages/core/src/ShortcutManager.ts | Implements cascade behavior, priority resolution, input ignoring, blur reset, and before/after hooks. |
| packages/core/src/ShortcutManager.test.ts | Adds extensive tests for cascade priority, sequences, blur reset, input handling, and hooks. |
| packages/core/src/ScopeManager.ts | Adds scope modes, priority records, sorting, and targeted popScope. |
| packages/core/src/index.ts | Expands exports and re-exports ShortcutManager as a named export. |
| packages/core/README.md | Overhauls core documentation to reflect new API surface and behaviors. |
| packages/core/package.json | Bumps @keybindy/core to 2.0.0. |
| packages/core/CHANGELOG.md | Adds v2.0.0 changelog entry describing core changes. |
| .github/workflows/release.yml | Adds Changesets-based release/publish workflow on main. |
| .github/workflows/ci.yml | Adds CI workflow for test/lint/build on pushes/PRs. |
| .changeset/config.json | Updates Changesets config (schema/changelog/linking) for coordinated releases. |
Suppressed comments (1)
packages/core/src/types.ts:353
HookOptions.keysuses the same mixed-shape type asShortcutBinding, which allows invalid combinations and makes hook filtering ambiguous. ReusingShortcutBindinghere keeps the contract consistent withregister()/unregister()and avoids mixed-array inputs.
* Target specific key combination(s) (e.g. `['Delete']` or `[['Ctrl', 'S'], ['Meta', 'S']]`).
* If omitted or empty, applies to all shortcuts in the scope.
*/
keys?: (Key | Key[])[];
};
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 1 comment.
Suppressed comments (5)
Previously missed (3) — in code that hasn't changed since the last review.
packages/core/src/ShortcutManager.ts:431
beforeEachhooks can block hold-shortcut cleanup on keyup: if a guard returnsfalse,activeHoldShortcuts.delete(shortcutId)is skipped, leaving the hold shortcut stuck "down" and preventing future activations.
if (shortcut && !this.shouldIgnoreForInput(shortcut, e.target) && shortcut.keys.map(k => k.toLowerCase()).includes(key)) {
if (this.runBeforeHooks(shortcut, e)) {
if (shortcut.options?.preventDefault) e.preventDefault();
(shortcut.handler as HoldShortcutHandler)(e, 'up');
this.activeHoldShortcuts.delete(shortcutId);
packages/react/src/useShortcuts.ts:251
ignoreInputsis used when registering shortcuts, but it is not included in the effect dependency array. If a component togglesignoreInputs, existing registrations won't be updated (and this also violates React hooks dependency expectations).
}, [scope, manager, disabled, priority, scopeMode, Boolean(beforeEach), Boolean(afterEach), stableShortcuts]);
packages/core/README.md:219
- Docs say
silentdefaults totrue, butShortcutManagercurrently defaultssilent = falsein the constructor. This makes the README option defaults inaccurate for core users.
silent?: boolean; // Default: true (suppress debug logs)
packages/react/src/useShortcuts.ts:170
handlersRef.currentis only ever appended/overwritten by key and is never cleared. If theshortcutslist changes over time (dynamic registrations with different key sets), this ref can grow unbounded for the lifetime of the component.
const handlersRef = React.useRef<Record<string, ShortcutHandler>>({});
shortcuts.forEach(({ keys, handler }) => {
handlersRef.current[JSON.stringify(keys)] = handler;
});
tests/sample-react/src/App.jsx:132
- Typo in log message: "presed" → "pressed".
return (<Keybindy scope='dialog' shortcuts={[{ keys: ["X"], handler: () => { console.log("x presed from model") } }]}>
v2.0.0 - cascade mode, priority scopes, hooks, zero-blinking react architecture, and ci/cd workflows