fix: auto-resize comment textarea as content is typed#220
Conversation
Adds a JS-based auto-resize fallback for the comment input textareas, since field-sizing: content has limited browser support. Closes #218. Made-with: Cursor
|
This run croaked 😵 The workflow encountered an error before any progress could be reported. Please check the link below for details. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@react-grab/cli
grab
@react-grab/ami
@react-grab/amp
@react-grab/claude-code
@react-grab/codex
@react-grab/copilot
@react-grab/cursor
@react-grab/droid
@react-grab/gemini
@react-grab/opencode
react-grab
@react-grab/relay
@react-grab/utils
commit: |
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/react-grab/src/components/selection-label/index.tsx">
<violation number="1" location="packages/react-grab/src/components/selection-label/index.tsx:449">
P2: The `autoResizeTextarea` fallback is missing for the copying-state textarea. In browsers without `field-sizing: content`, this textarea will fail to expand to fit its initial content on mount.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| "field-sizing": "content", | ||
| "min-height": "16px", | ||
| "max-height": "95px", | ||
| "max-height": `${TEXTAREA_MAX_HEIGHT_PX}px`, |
There was a problem hiding this comment.
P2: The autoResizeTextarea fallback is missing for the copying-state textarea. In browsers without field-sizing: content, this textarea will fail to expand to fit its initial content on mount.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/components/selection-label/index.tsx, line 449:
<comment>The `autoResizeTextarea` fallback is missing for the copying-state textarea. In browsers without `field-sizing: content`, this textarea will fail to expand to fit its initial content on mount.</comment>
<file context>
@@ -440,7 +446,7 @@ export const SelectionLabel: Component<SelectionLabelProps> = (props) => {
"field-sizing": "content",
"min-height": "16px",
- "max-height": "95px",
+ "max-height": `${TEXTAREA_MAX_HEIGHT_PX}px`,
"scrollbar-width": "none",
}}
</file context>
Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
There was a problem hiding this comment.
3 issues found across 19 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/react-grab/src/core/plugin-registry.ts">
<violation number="1" location="packages/react-grab/src/core/plugin-registry.ts:124">
P1: SolidJS `setStore` will execute `getContent` immediately instead of storing it because functions passed as the final argument are treated as state updaters.</violation>
</file>
<file name="packages/react-grab/src/components/tooltip.tsx">
<violation number="1" location="packages/react-grab/src/components/tooltip.tsx:20">
P2: `lastCloseTimestamp` is now per-instance instead of the previous module-level `tooltipCloseTimestamp`. This breaks the cross-tooltip grace period ("warm tooltips") behavior: closing one tooltip and quickly hovering an adjacent toolbar button will no longer skip the delay, because each `Tooltip` instance tracks its own close time independently. Keep this as a shared module-level variable so the grace period works across all toolbar tooltips.</violation>
</file>
<file name="packages/react-grab/src/utils/is-mac.ts">
<violation number="1" location="packages/react-grab/src/utils/is-mac.ts:27">
P2: Add `iOS` to the platform regex to correctly match iOS devices when `userAgentData.platform` returns `"iOS"`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| optionValue: OptionsState[OptionKey], | ||
| ) => { | ||
| directOptionOverrides[optionKey] = optionValue; | ||
| setStore("options", optionKey, optionValue); |
There was a problem hiding this comment.
P1: SolidJS setStore will execute getContent immediately instead of storing it because functions passed as the final argument are treated as state updaters.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/core/plugin-registry.ts, line 124:
<comment>SolidJS `setStore` will execute `getContent` immediately instead of storing it because functions passed as the final argument are treated as state updaters.</comment>
<file context>
@@ -113,17 +116,39 @@ const createPluginRegistry = (initialOptions: SettableOptions = {}) => {
+ optionValue: OptionsState[OptionKey],
+ ) => {
+ directOptionOverrides[optionKey] = optionValue;
+ setStore("options", optionKey, optionValue);
+ };
+
</file context>
| setStore("options", optionKey, optionValue); | |
| setStore("options", optionKey, () => optionValue); |
| getPlatformFromUserAgentData() ?? | ||
| navigator.platform ?? | ||
| navigator.userAgent; | ||
| cachedIsMac = /Mac|iPhone|iPad|iPod/i.test(platform); |
There was a problem hiding this comment.
P2: Add iOS to the platform regex to correctly match iOS devices when userAgentData.platform returns "iOS".
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/utils/is-mac.ts, line 27:
<comment>Add `iOS` to the platform regex to correctly match iOS devices when `userAgentData.platform` returns `"iOS"`.</comment>
<file context>
@@ -1,10 +1,30 @@
+ getPlatformFromUserAgentData() ??
+ navigator.platform ??
+ navigator.userAgent;
+ cachedIsMac = /Mac|iPhone|iPad|iPod/i.test(platform);
}
return cachedIsMac;
</file context>
| cachedIsMac = /Mac|iPhone|iPad|iPod/i.test(platform); | |
| cachedIsMac = /Mac|iOS|iPhone|iPad|iPod/i.test(platform); |
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/react-grab/src/components/toolbar/toolbar-content.tsx">
<violation number="1">
P2: Change `transition-transform` to `transition-all` so the `left` position change is properly animated.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
… and ensure consistent rendering of icons
There was a problem hiding this comment.
3 issues found across 7 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/react-grab/src/components/toolbar/toolbar-content.tsx">
<violation number="1" location="packages/react-grab/src/components/toolbar/toolbar-content.tsx:257">
P2: Add the `"pointer-events-none"` parameter to the `selectButton`'s `expandGridClass` to prevent ghost clicks while collapsed.</violation>
<violation number="2" location="packages/react-grab/src/components/toolbar/toolbar-content.tsx:287">
P2: Add `props.enabled` check to `isCopyAllExpanded` so the 'Copy All' button correctly collapses when the toolbar is disabled.</violation>
</file>
<file name="packages/gym/app/layout.tsx">
<violation number="1" location="packages/gym/app/layout.tsx:4">
P1: Importing a client-side side-effect module directly in a Server Component prevents it from executing in the browser. Since `react-grab` lacks a `"use client"` directive, this import will be stripped from the client bundle and fail to initialize the utility.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| import { Geist, Geist_Mono } from "next/font/google"; | ||
| import { ThemeProvider } from "@/components/theme-provider"; | ||
| import { ReactGrab } from "react-grab/react"; | ||
| import "react-grab"; |
There was a problem hiding this comment.
P1: Importing a client-side side-effect module directly in a Server Component prevents it from executing in the browser. Since react-grab lacks a "use client" directive, this import will be stripped from the client bundle and fail to initialize the utility.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/gym/app/layout.tsx, line 4:
<comment>Importing a client-side side-effect module directly in a Server Component prevents it from executing in the browser. Since `react-grab` lacks a `"use client"` directive, this import will be stripped from the client bundle and fail to initialize the utility.</comment>
<file context>
@@ -1,7 +1,7 @@
import { Geist, Geist_Mono } from "next/font/google";
import { ThemeProvider } from "@/components/theme-provider";
-import { ReactGrab } from "react-grab/react";
+import "react-grab";
import "./globals.css";
</file context>
| class={cn( | ||
| "grid", | ||
| gridTransitionClass(), | ||
| expandGridClass(Boolean(props.enabled)), |
There was a problem hiding this comment.
P2: Add the "pointer-events-none" parameter to the selectButton's expandGridClass to prevent ghost clicks while collapsed.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/components/toolbar/toolbar-content.tsx, line 257:
<comment>Add the `"pointer-events-none"` parameter to the `selectButton`'s `expandGridClass` to prevent ghost clicks while collapsed.</comment>
<file context>
@@ -159,16 +247,71 @@ export const ToolbarContent: Component<ToolbarContentProps> = (props) => {
+ class={cn(
+ "grid",
+ gridTransitionClass(),
+ expandGridClass(Boolean(props.enabled)),
+ )}
+ >
</file context>
| expandGridClass(Boolean(props.enabled)), | |
| expandGridClass(Boolean(props.enabled), "pointer-events-none"), |
| "grid", | ||
| gridTransitionClass(), | ||
| expandGridClass( | ||
| Boolean(props.isCopyAllExpanded), |
There was a problem hiding this comment.
P2: Add props.enabled check to isCopyAllExpanded so the 'Copy All' button correctly collapses when the toolbar is disabled.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/components/toolbar/toolbar-content.tsx, line 287:
<comment>Add `props.enabled` check to `isCopyAllExpanded` so the 'Copy All' button correctly collapses when the toolbar is disabled.</comment>
<file context>
@@ -159,16 +247,71 @@ export const ToolbarContent: Component<ToolbarContentProps> = (props) => {
+ "grid",
+ gridTransitionClass(),
+ expandGridClass(
+ Boolean(props.isCopyAllExpanded),
+ "pointer-events-none",
+ ),
</file context>
| Boolean(props.isCopyAllExpanded), | |
| Boolean(props.enabled) && Boolean(props.isCopyAllExpanded), |
There was a problem hiding this comment.
2 issues found across 34 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/demo/components/dashboard.tsx">
<violation number="1" location="packages/demo/components/dashboard.tsx:123">
P2: Change the variant to fix the visibility bug noted in the comment directly above (e.g., use 'outline' or 'secondary' instead of 'ghost').</violation>
<violation number="2" location="packages/demo/components/dashboard.tsx:126">
P3: Use `console.warn` instead of throwing a raw error for unimplemented features to prevent triggering error trackers and polluting the console with uncaught exceptions.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| <Button size="sm" variant="ghost" className="" onClick={() => { throw new Error("exportDashboard is not a function"); }}> | ||
| <Button | ||
| size="sm" | ||
| variant="ghost" |
There was a problem hiding this comment.
P2: Change the variant to fix the visibility bug noted in the comment directly above (e.g., use 'outline' or 'secondary' instead of 'ghost').
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/demo/components/dashboard.tsx, line 123:
<comment>Change the variant to fix the visibility bug noted in the comment directly above (e.g., use 'outline' or 'secondary' instead of 'ghost').</comment>
<file context>
@@ -118,7 +118,14 @@ export function Dashboard() {
- <Button size="sm" variant="ghost" className="" onClick={() => { throw new Error("exportDashboard is not a function"); }}>
+ <Button
+ size="sm"
+ variant="ghost"
+ className=""
+ onClick={() => {
</file context>
| variant="ghost" | |
| variant="outline" |
| variant="ghost" | ||
| className="" | ||
| onClick={() => { | ||
| throw new Error("exportDashboard is not a function"); |
There was a problem hiding this comment.
P3: Use console.warn instead of throwing a raw error for unimplemented features to prevent triggering error trackers and polluting the console with uncaught exceptions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/demo/components/dashboard.tsx, line 126:
<comment>Use `console.warn` instead of throwing a raw error for unimplemented features to prevent triggering error trackers and polluting the console with uncaught exceptions.</comment>
<file context>
@@ -118,7 +118,14 @@ export function Dashboard() {
+ variant="ghost"
+ className=""
+ onClick={() => {
+ throw new Error("exportDashboard is not a function");
+ }}
+ >
</file context>
| throw new Error("exportDashboard is not a function"); | |
| console.warn("exportDashboard is not a function"); |
…ctionality - Added tests to verify ArrowUp traversal to parent elements and ensure repeated presses do not cause oscillation. - Implemented checks to confirm that ArrowUp bounds do not shrink during navigation. - Added tests for ArrowDown to ensure it correctly reverses ArrowUp actions while maintaining selection visibility.
There was a problem hiding this comment.
2 issues found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/react-grab/src/components/selection-label/arrow-navigation-menu.tsx">
<violation number="1" location="packages/react-grab/src/components/selection-label/arrow-navigation-menu.tsx:74">
P2: The first item hovered when the mouse enters the menu will fail to select as active because `pointerenter` fires before `pointermove` can set `didPointerMove` to true.</violation>
</file>
<file name="packages/react-grab/e2e/keyboard-navigation.spec.ts">
<violation number="1" location="packages/react-grab/e2e/keyboard-navigation.spec.ts:338">
P2: The test does not assert that the selection actually reversed. It only verifies that a selection box is visible, which would pass even if ArrowDown navigated to the wrong element or did nothing.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| onPointerEnter={(event) => { | ||
| updateHighlight(event.currentTarget); | ||
| props.onSelect(itemIndex()); | ||
| if (didPointerMove) { |
There was a problem hiding this comment.
P2: The first item hovered when the mouse enters the menu will fail to select as active because pointerenter fires before pointermove can set didPointerMove to true.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/components/selection-label/arrow-navigation-menu.tsx, line 74:
<comment>The first item hovered when the mouse enters the menu will fail to select as active because `pointerenter` fires before `pointermove` can set `didPointerMove` to true.</comment>
<file context>
@@ -61,7 +71,9 @@ export const ArrowNavigationMenu: Component<ArrowNavigationMenuProps> = (
onPointerEnter={(event) => {
updateHighlight(event.currentTarget);
- props.onSelect(itemIndex());
+ if (didPointerMove) {
+ props.onSelect(itemIndex());
+ }
</file context>
| const afterDownVisible = await reactGrab.isSelectionBoxVisible(); | ||
| expect(afterDownVisible).toBe(true); | ||
|
|
||
| const afterDownBounds = await reactGrab.getSelectionBoxBounds(); |
There was a problem hiding this comment.
P2: The test does not assert that the selection actually reversed. It only verifies that a selection box is visible, which would pass even if ArrowDown navigated to the wrong element or did nothing.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/e2e/keyboard-navigation.spec.ts, line 338:
<comment>The test does not assert that the selection actually reversed. It only verifies that a selection box is visible, which would pass even if ArrowDown navigated to the wrong element or did nothing.</comment>
<file context>
@@ -236,3 +236,108 @@ test.describe("Navigation History and Wrapping", () => {
+ const afterDownVisible = await reactGrab.isSelectionBoxVisible();
+ expect(afterDownVisible).toBe(true);
+
+ const afterDownBounds = await reactGrab.getSelectionBoxBounds();
+ expect(afterDownBounds).not.toBeNull();
+ expect(afterDownBounds!.width).toBeGreaterThan(0);
</file context>

Summary
autoResizeTextareautility that adjusts textarea height on input, providing a cross-browser fallback forfield-sizing: content(limited browser support)TEXTAREA_MAX_HEIGHT_PXconstant to replace the magic"95px"across all three textareasCloses #218
Test plan
field-sizing: content(e.g. Firefox < 131) — auto-resize should still workMade with Cursor
Note
Medium Risk
Touches core overlay UX (toolbar, menus, arrow navigation) and changes the
react-grabpublic entrypoints/exports, so regressions could affect activation, positioning, and integrations despite mostly UI-focused logic.Overview
Improves comment/prompt textarea behavior by introducing
autoResizeTextarea, centralizingTEXTAREA_MAX_HEIGHT_PX, and resizing on mount/input to provide a cross-browser fallback forfield-sizing: content.Refactors overlay UI interactions: extracts reusable
ToolbarContent, adds cycling selection hints, tightens toolbar state persistence/validation defaults, and standardizes menu event suppression viasuppressMenuEventwhile cleaning up animation/timeout lifecycles.Fixes selection/navigation robustness by probing with visible bounds centers (new
getVisibleBoundsCenter) and adds Playwright coverage for ArrowUp traversal. Also hardens misc utilities (saferdata-optionsparsing,execCommandguard, improved Mac detection, typedonIdle) and removes the deprecatedreact-grab/reactentry/build in favor of side-effectimport "react-grab".Written by Cursor Bugbot for commit 21793d6. This will update automatically on new commits. Configure here.
Summary by cubic
Adds JS auto-resize to all comment textareas so they grow with input (up to 95px) and work consistently across browsers. Also improves toolbar history/copy/menu behavior, stabilizes arrow-key navigation, and adds coverage to prevent regressions. Closes #218.
Bug Fixes
Refactors
Written for commit 21793d6. Summary will update on new commits.