-
Notifications
You must be signed in to change notification settings - Fork 189
feat: add customizable keyboard shortcuts, multi-select elements, and modern ChatInput #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add customizable keyboard shortcuts, multi-select elements, and modern ChatInput #97
Conversation
Add utility for detecting user's operating system (Mac, Windows, Linux) with caching for performance. Used for platform-aware keyboard shortcuts.
Add utilities for formatting keyboard shortcuts with platform-specific symbols (⌘, ⌥, ⇧ for Mac, Ctrl+Alt+Shift for Windows/Linux).
Add shortcut configuration system with: - localStorage persistence for user preferences - Platform-aware default shortcuts (Cmd+Ctrl on Mac, Ctrl+Shift+G on Windows/Linux) - Versioned storage format for future migrations
Add icon components for enhanced UI: - IconClose: X icon for remove/dismiss actions - IconSend: Arrow icon for submit actions - IconSparkles: AI indicator icon - IconSettings: Gear icon for settings panel
Add TypeScript interfaces for new features: - RequiredActivationKey: strict shortcut definition - SelectedElementInfo: metadata for multi-selected elements - ElementListProps, ElementListItemProps: element list components - ChatInputProps: modern input component - Extended ReactGrabAPI with updateShortcut/getShortcut methods - Extended ReactGrabRendererProps with multi-select and shortcut props
Add ChatInput component with: - Auto-resizing textarea - Keyboard shortcuts (Enter to submit, Shift+Enter for new line, Esc to cancel) - Tab focus trap between input and submit button - AI status indicator with loading state - Clean, modern UI design
Add components for displaying multiple selected elements: - ElementList: expandable/collapsible list with element count - ElementListItem: displays component name, tag, file location - Click to jump to source file - Remove button on hover - Smooth hover animations
Add keyboard shortcut configuration panel: - Record new shortcuts by pressing keys - Requires at least one modifier key - Save to localStorage for persistence - Reset to platform-specific defaults - Visual feedback during recording
Update getRequiredModifiers to use platform detection: - Mac: Cmd+Ctrl (metaKey + ctrlKey) - Windows/Linux: Ctrl+Shift (ctrlKey + shiftKey)
Add XState actions and events for multi-select: - TOGGLE_ELEMENT_SELECTION: add/remove elements from selection - TOGGLE_ELEMENT_LIST: expand/collapse element list - REMOVE_ELEMENT_FROM_SELECTION: remove specific element - isElementListExpanded context property - Auto-collapse list when only 1 element remains
Add updateShortcut and getShortcut methods to the noop API for when react-grab is disabled but API interface is needed.
Integrate ShortcutSettings panel in toolbar: - Add settings gear button - Toggle settings panel on click - Pass shortcut props through toolbar interface
Replace inline textarea with ChatInput component for modern UX. Add ElementList for multi-select mode showing selected elements with expand/collapse and remove functionality.
Add prop forwarding for: - Multi-select element list props (selectedElements, callbacks) - Shortcut configuration props (currentShortcut, onShortcutChange)
Add core functionality for new features: - Load/save shortcut configuration with localStorage persistence - Create selectedElementsInfo resource with component metadata - Calculate aggregateSelectionBounds for multi-select positioning - Add updateShortcut/getShortcut to public API - Track shift-click for multi-select race condition prevention
Export new public APIs: - detectPlatform, formatShortcut utilities - getDefaultShortcut, loadShortcutConfig, saveShortcutConfig, clearShortcutConfig - ActivationKey, RequiredActivationKey types
|
@RobertWsp is attempting to deploy a commit to the Million Team on Vercel. A member of the Team first needs to authorize it. |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. Comment |
Summary of ChangesHello @RobertWsp, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request delivers a substantial upgrade to the Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Mesa DescriptionTL;DRIntroduces customizable keyboard shortcuts for activation, multi-select elements with Shift+Click, and a modern, auto-resizing ChatInput component. These changes enhance the user experience with platform-aware defaults, persistent configuration, and improved selection and keyboard workflows. What changed?
Description generated by Mesa. Update settings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This is an impressive pull request that adds significant new functionality: customizable keyboard shortcuts, multi-element selection, and a modern chat input. The code is well-structured, with new features encapsulated in their own components and modules. The core logic and state machine have been updated thoughtfully to integrate these new capabilities.
I've found a few areas for improvement, mainly related to code clarity, adherence to SolidJS best practices, and a minor logical correction. My comments include suggestions to reduce code duplication in the new ChatInput component, improve conditional rendering in ElementListItem, fix an issue with borderRadius calculation for multi-selections, and clean up an unused prop in your type definitions.
Overall, this is a great contribution. Addressing these minor points will make the new code even more robust and maintainable.
packages/react-grab/src/components/selection-label/chat-input.tsx
Outdated
Show resolved
Hide resolved
packages/react-grab/src/components/selection-label/chat-input.tsx
Outdated
Show resolved
Hide resolved
packages/react-grab/src/components/selection-label/element-list-item.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performed full review of 9c642a3...d793336
Analysis
-
Race Condition Risk: Module-level variable
wasShiftClickOnMousedowncould cause issues with multiple instances or rapid interactions and should be moved to the machine context or a ref. -
Performance Concerns:
selectedElementsInfoperforms expensive async operations on every viewport change, and aggregate bounds calculation recalculates frequently. Both need optimization through memoization or debouncing. -
Duplicate Logic: Platform detection logic is duplicated between
getRequiredModifiers()andgetDefaultShortcut(), violating DRY principles and risking inconsistent behavior. -
Error Handling Gaps: Silent failures in localStorage operations within
shortcut/state.tslack proper user feedback, potentially causing confusion. -
Missing Validation: No validation exists to prevent conflicts between custom shortcuts and browser/OS shortcuts (e.g., Cmd+Q, Cmd+W).
Tip
Help
Slash Commands:
/review- Request a full code review/review latest- Review only changes since the last review/describe- Generate PR description. This will update the PR body or issue comment depending on your configuration/help- Get help with Mesa commands and configuration options
0 files reviewed | 3 comments | Edit Agent Settings • Read Docs
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…t-item.tsx Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Greptile SummaryThis PR introduces three cohesive enhancements: customizable keyboard shortcuts with platform-aware defaults and persistent configuration, Shift+Click multi-select with an expandable element list, and a modern ChatInput component with auto-resize and keyboard shortcuts. Key Changes:
Architecture:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Toolbar
participant ShortcutSettings
participant LocalStorage
participant Core
participant Machine
participant Renderer
participant SelectionLabel
participant ElementList
participant ChatInput
Note over User,ChatInput: Shortcut Customization Flow
User->>Toolbar: Click settings icon
Toolbar->>ShortcutSettings: Open settings panel
User->>ShortcutSettings: Click record button
ShortcutSettings->>ShortcutSettings: Start recording
User->>ShortcutSettings: Press Cmd+Ctrl+K
ShortcutSettings->>ShortcutSettings: Validate modifiers
ShortcutSettings->>LocalStorage: saveShortcutConfig()
ShortcutSettings->>Core: updateShortcut()
ShortcutSettings->>Toolbar: Close settings
Note over User,ChatInput: Multi-Select Flow
User->>Core: Activate (Cmd+Ctrl)
Core->>Machine: ACTIVATE
Machine->>Renderer: Update state
User->>Core: Shift+Click element 1
Core->>Machine: TOGGLE_ELEMENT_SELECTION
Machine->>Machine: toggleElementSelection
User->>Core: Shift+Click element 2
Core->>Machine: TOGGLE_ELEMENT_SELECTION
Machine->>Machine: Add to frozenElements
Core->>Renderer: Update with 2 elements
Renderer->>SelectionLabel: Show with selectedElements
SelectionLabel->>ElementList: Render element list
User->>ElementList: Click expand button
ElementList->>Core: onToggle()
Core->>Machine: TOGGLE_ELEMENT_LIST
Machine->>Machine: Toggle isElementListExpanded
Note over User,ChatInput: ChatInput & Submit Flow
User->>SelectionLabel: Double-click or Enter
SelectionLabel->>ChatInput: Show input
User->>ChatInput: Type prompt
ChatInput->>SelectionLabel: onInput(value)
SelectionLabel->>Core: onInputChange(value)
Core->>Machine: INPUT_CHANGE
User->>ChatInput: Press Enter
ChatInput->>SelectionLabel: onSubmit()
SelectionLabel->>Core: onSubmit()
Core->>Core: copyElementsToClipboard()
Core->>Machine: COPY_START
Core->>Machine: COPY_DONE
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional Comments (1)
-
packages/react-grab/src/utils/detect-platform.ts, line 22 (link)logic:
isLinuxdefaulting totruetreats all unknown platforms (mobile, tablets, ChromeOS, etc.) as Linux
20 files reviewed, 1 comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/react-grab/src/components/selection-label/element-list-item.tsx">
<violation number="1" location="packages/react-grab/src/components/selection-label/element-list-item.tsx:66">
P1: In SolidJS, when using `<Show>` with a callback function without the `keyed` prop, the parameter is an accessor function that must be called. Use `{location()}` instead of `{location}`, or add the `keyed` prop to receive the value directly.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
packages/react-grab/src/components/selection-label/element-list-item.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
10 issues found across 20 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="packages/react-grab/src/utils/detect-platform.ts">
<violation number="1" location="packages/react-grab/src/utils/detect-platform.ts:17">
P1: Missing browser environment check will cause crashes during SSR. The `navigator` object doesn't exist in Node.js/SSR contexts. Add a guard at the start of the function to return a safe default when not in a browser.</violation>
</file>
<file name="packages/react-grab/src/components/selection-label/element-list.tsx">
<violation number="1" location="packages/react-grab/src/components/selection-label/element-list.tsx:39">
P2: Hardcoded `id` will cause duplicate IDs if multiple `ElementList` components are rendered. Use SolidJS's `createUniqueId()` to generate a unique ID per component instance for proper accessibility.</violation>
</file>
<file name="packages/react-grab/src/core/index.tsx">
<violation number="1" location="packages/react-grab/src/core/index.tsx:99">
P1: Shortcut initialization can desync UI vs actual activation behavior when both persisted config and options.activationKey exist, and the `as RequiredActivationKey` cast can introduce `undefined` modifier booleans at runtime. Normalize the option value and make `options.activationKey` and `currentShortcut` come from the same resolved shortcut.</violation>
<violation number="2" location="packages/react-grab/src/core/index.tsx:507">
P2: `selectedElementsInfo` refetches on every `viewportVersion` change, which can repeatedly recompute expensive async metadata (stack traces / component names) during scroll/resize. Consider decoupling: keep a metadata resource keyed only on `frozenElements`, and recompute only `bounds` in a memo keyed on `viewportVersion`.</violation>
<violation number="3" location="packages/react-grab/src/core/index.tsx:523">
P2: The ID generation `el-${index}-${Date.now()}` creates unstable and potentially non-unique IDs. All elements in the same batch share the same timestamp, and IDs change on every resource re-run, which can cause unnecessary re-renders and key conflicts in the element list.
Consider using a stable identifier based on the element itself (e.g., a WeakMap-based ID generator or element reference hash).</violation>
</file>
<file name="packages/react-grab/src/components/toolbar/index.tsx">
<violation number="1" location="packages/react-grab/src/components/toolbar/index.tsx:484">
P2: The settings gear button should only be shown (or disabled) when `currentShortcut` and `onShortcutChange` are provided. Also, mirror the other toolbar buttons’ click handling by adding `stopImmediatePropagation()` + a `didDragOccur` guard, and set `type="button"` to avoid accidental form submits.</violation>
<violation number="2" location="packages/react-grab/src/components/toolbar/index.tsx:517">
P2: Settings panel remains visible when toolbar is collapsed. Add `!isCollapsed()` to the condition to hide the settings panel when the toolbar collapses, preventing a confusing floating UI state.</violation>
</file>
<file name="packages/react-grab/src/components/selection-label/index.tsx">
<violation number="1" location="packages/react-grab/src/components/selection-label/index.tsx:491">
P1: Duplicate keyboard handling: passing `handleKeyDown` to ChatInput causes both parent and child handlers to fire. Escape will call both `onConfirmDismiss` and `onCancel`, and Enter will call `onSubmit` twice. Since ChatInput already handles Enter/Escape internally, remove the `onKeyDown` prop and use `onConfirmDismiss` for cancel to maintain the original behavior.</violation>
</file>
<file name="packages/react-grab/src/shortcut/state.ts">
<violation number="1" location="packages/react-grab/src/shortcut/state.ts:67">
P2: Empty catch block silently swallows errors. Consider logging the error or adding a comment explaining why errors are intentionally ignored to aid future debugging.</violation>
</file>
<file name="packages/react-grab/src/core/machine.ts">
<violation number="1" location="packages/react-grab/src/core/machine.ts:306">
P1: When toggling *off* a selected element, `frozenElement` is always reset to `remaining[0]`, which can unexpectedly change the primary selection even if the removed element wasn’t the active one. Preserve `context.frozenElement` unless it’s the element being removed (or it’s no longer in the remaining selection).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…t-item.tsx Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Summary
This PR introduces three major features to enhance the react-grab experience:
🎹 Customizable Keyboard Shortcuts
⌘+^, Windows/Linux usesCtrl+Shift+G🎯 Multi-Select Elements
💬 Modern ChatInput Component
New Files
utils/detect-platform.ts- OS detection utilityutils/format-shortcut.ts- Shortcut formatting with platform symbolsshortcut/state.ts- Shortcut persistence with localStoragecomponents/icons/icon-{close,send,sparkles,settings}.tsx- New iconscomponents/selection-label/chat-input.tsx- Modern input componentcomponents/selection-label/element-list{,-item}.tsx- Multi-select listcomponents/toolbar/shortcut-settings.tsx- Settings panelModified Files
types.ts- New interfaces for all featurescore/machine.ts- Multi-select XState actionscore/keyboard-handlers.ts- Platform-aware defaultscore/index.tsx- Core integrationcomponents/*/index.tsx- Component integrationsTest plan
🤖 Generated with Claude Code
Note
Introduces configurable activation shortcuts, multi-select selection UX, and a refreshed input, with core/state and UI integration.
localStorage, toolbar settings UI (ShortcutSettings), new utils (detect-platform,format-shortcut), APIsupdateShortcut/getShortcut, and exportsElementListwith open-file and remove, aggregate selection bounds, cursor behavior tweaks, and XState actions (TOGGLE_ELEMENT_SELECTION,TOGGLE_ELEMENT_LIST,REMOVE_ELEMENT_FROM_SELECTION)ChatInput(auto-resize, Enter/Shift+Enter/Esc, focus trap, AI status), integrated intoSelectionLabelicon-{close,send,sparkles,settings})keyboard-handlers, updated bounds/label calculations; extendedtypes.tsfor shortcuts, element list, and chat inputWritten by Cursor Bugbot for commit b0799c0. This will update automatically on new commits. Configure here.
Summary by cubic
Adds customizable activation shortcuts, Shift+Click multi-select with an element list, and a modern ChatInput to improve selection and keyboard workflows. This brings platform-aware defaults and persistent settings, plus a cleaner input experience.
New Features
Migration
Written for commit 9755b1b. Summary will update on new commits.