feat(mobile): expand demo screen with all UI components#1262
Conversation
Exercise every UI component brought into the project (Button, Badge, Alert, Avatar, Input, Textarea, Checkbox, Switch, RadioGroup, Select, Progress, Tabs, Accordion, Collapsible, Dialog, AlertDialog, Popover, Tooltip, Toggle, ToggleGroup, Skeleton, AspectRatio, Separator) alongside existing live data sections. Add Expo MCP server to project config. Includes biome lint fixes across UI components (unnecessary fragments, missing deps).
📝 WalkthroughWalkthroughThis PR introduces a comprehensive cross-platform mobile UI component library built on Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Use the simple `maestro mcp` command instead of hardcoded user paths, so it works for any developer with maestro on their PATH.
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
Rename to `placeholderTextColorClassName` to match the actual uniwind type augmentation. Remove unused destructured prop from input.tsx.
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/mobile/components/ui/input.tsx (1)
4-31:⚠️ Potential issue | 🟡 Minor
placeholderClassNameis destructured but never passed toTextInput.The prop is extracted on Line 6 and silently discarded. Compare with
textarea.tsx(Line 21) which correctly forwards it:placeholderClassName={cn("text-muted-foreground", placeholderClassName)}. This looks like an oversight.🐛 Proposed fix
<TextInput className={cn( "dark:bg-input/30 border-input bg-background text-foreground flex h-10 w-full min-w-0 flex-row items-center rounded-md border px-3 py-1 text-base leading-5 shadow-sm shadow-black/5 sm:h-9", props.editable === false && cn( "opacity-50", Platform.select({ web: "disabled:pointer-events-none disabled:cursor-not-allowed", }), ), Platform.select({ web: cn( "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground outline-none transition-[color,box-shadow] md:text-sm", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", ), native: "placeholder:text-muted-foreground/50", }), className, )} + placeholderClassName={cn("text-muted-foreground", placeholderClassName)} {...props} />
🤖 Fix all issues with AI agents
In `@apps/mobile/components/ui/alert.tsx`:
- Around line 22-27: The provider value for TextClassContext currently includes
the Alert component's className (cn(..., className)), which leaks outer
View/layout classes into descendant text styling; remove className from the
TextClassContext.Provider value and instead keep only text-related classes
(e.g., cn("text-sm text-foreground", variant === "destructive" &&
"text-destructive")). If you need consumer control over text classes, add an
explicit prop like textClassName and include that in the provider value; ensure
the original className continues to be applied only to the outer View/container.
In `@apps/mobile/components/ui/dropdown-menu.tsx`:
- Around line 133-145: The DropdownMenuPrimitive.Content className uses
context-menu CSS variables incorrectly; update the CSS variable names inside the
className string for DropdownMenuPrimitive.Content (the block rendering
DropdownMenuContent) by replacing --radix-context-menu-content-available-height
and --radix-context-menu-content-transform-origin with their dropdown-menu
equivalents (--radix-dropdown-menu-content-available-height and
--radix-dropdown-menu-content-transform-origin) so the animation and max-height
origin use the correct variables while preserving the Platform.select logic and
existing props/className merging.
- Around line 86-99: The className uses the wrong Radix CSS variable name
causing incorrect transform origin; in the web platform class strings for
DropdownMenuPrimitive.SubContent (inside NativeOnlyAnimatedView) and the sibling
DropdownMenuContent component, replace occurrences of
"--radix-context-menu-content-transform-origin" with
"--radix-dropdown-menu-content-transform-origin" (i.e., update the origin-(...)
token) so the dropdown menu uses the correct per-component CSS custom property
for transform origin.
In `@apps/mobile/components/ui/icon.tsx`:
- Around line 24-37: Update the JSDoc example import path to match the actual
module export for the Icon component: locate the JSDoc block above the Icon
component (search for "A wrapper component for Lucide icons" and the Icon
symbol) and change the example import from
"@/registry/uniwind/registry/components/ui/icon" to the correct module path that
exports Icon (the actual path used in the codebase where Icon is exported), so
the example imports the Icon component from the real module.
In `@apps/mobile/components/ui/label.tsx`:
- Around line 1-13: The file uses React.RefAttributes in the Label component
signature but React is not imported; add the missing import (import * as React
from "react") at the top of the file so React.RefAttributes and any other React
types work correctly in the Label function declaration and its props handling.
In `@apps/mobile/components/ui/menubar.tsx`:
- Around line 155-169: The MenubarSubContent component uses the wrong Radix CSS
variable name; in the JSX returned by MenubarPrimitive.SubContent inside
NativeOnlyAnimatedView update the origin utility that currently references
--radix-context-menu-content-transform-origin to use the menubar-specific
variable (e.g., --radix-menubar-content-transform-origin) so the
transform-origin matches the Menubar primitives; locate the className string
inside the MenubarPrimitive.SubContent render (the Platform.select web branch)
and replace the context-menu variable with the menubar variant.
- Around line 195-217: The Menubar content CSS uses context-menu variables;
update MenubarPrimitive.Content's className to replace
--radix-context-menu-content-available-height and
--radix-context-menu-content-transform-origin with the menubar equivalents
--radix-menubar-content-available-height and
--radix-menubar-content-transform-origin (keep the surrounding cn(...)
composition, Platform.select block, and the props.side conditional logic intact
so only the variable names change).
In `@apps/mobile/components/ui/select.tsx`:
- Around line 169-194: SelectItem currently destructures children but never
renders them, so labels like "Apple" passed from DemoScreen are ignored; fix by
rendering children inside SelectPrimitive.ItemText (replace the self-closing
<SelectPrimitive.ItemText ... /> with <SelectPrimitive.ItemText className="..."
>{children}</SelectPrimitive.ItemText>) so the passed children are displayed;
alternatively, if children should not be accepted, remove children from the
SelectItem parameter list and its type (SelectPrimitive.ItemProps &
React.RefAttributes<SelectPrimitive.ItemRef>) to stop accepting them.
In `@apps/mobile/components/ui/separator.tsx`:
- Around line 9-10: Add a React type import so the type reference
React.RefAttributes compiles: update the imports at the top of the separator
component file to import React for types (e.g., import * as React from "react"
or import React from "react") so the annotation SeparatorPrimitive.RootProps &
React.RefAttributes<SeparatorPrimitive.RootRef> is valid; ensure the import is
present alongside any existing SeparatorPrimitive imports in
components/ui/separator.tsx.
🧹 Nitpick comments (8)
apps/streams/package.json (1)
15-24: Dependency changes appear unrelated to the mobile UI components PR scope.These version bumps and the new
@hono/node-serverdependency are inapps/streams/, which doesn't seem connected to the stated PR objective of expanding the mobile demo screen. Consider splitting these into a separate PR to keep diffs minimal and focused. As per coding guidelines, "Keep diffs minimal with targeted edits only, avoiding unnecessary changes outside the scope of the task."apps/mobile/components/ui/alert.tsx (1)
68-83: Fragile string matching on context class names.
textClass?.includes("text-destructive")(Line 77) is a brittle way to determine variant state — it could false-positive on classes like"text-destructive-foreground". Consider passing the variant through a dedicated context or prop instead.apps/mobile/components/ui/progress.tsx (1)
65-75: Consider refactoring the animation pattern for better semantics.Line 70-71:
withSpringdoes accept string values (per API docs:AnimatableValue = number | string | number[]), so the current code is valid. However, for percentage-based numeric animations, it's more idiomatic to animate the numeric value and format it to a string afterwards, rather than animating a template string. This makes the animation semantics clearer.Line 66:
useDerivedValue(() => value ?? 0)is valid without a dependency array per the Reanimated docs (dependencies are optional for worklets reading props when the Babel plugin is configured). However, adding[value]would improve clarity and match the pattern in theuseAnimatedStylebelow.Suggested refactoring
function NativeIndicator({ value, className }: IndicatorProps) { - const progress = useDerivedValue(() => value ?? 0); + const progress = useDerivedValue(() => value ?? 0, [value]); const indicator = useAnimatedStyle(() => { + const widthValue = interpolate(progress.value, [0, 100], [1, 100], Extrapolation.CLAMP); return { - width: withSpring( - `${interpolate(progress.value, [0, 100], [1, 100], Extrapolation.CLAMP)}%`, - { overshootClamping: true }, - ), + width: `${withSpring(widthValue, { overshootClamping: true })}%`, }; }, [value]);apps/mobile/components/ui/tooltip.tsx (1)
33-43: Native animation only distinguishestopvs. non-topsides.On native,
side === "left"orside === "right"will getFadeInUp(a vertical animation), while the web path correctly handles all four directions with distinct slide-in classes. If horizontal tooltips are ever used on native, the enter animation will feel slightly off. Fine to defer if horizontal tooltips aren't planned for mobile.apps/mobile/components/ui/toggle-group.tsx (1)
50-104: Consider??instead of||for variant/size fallback.Lines 79-80 use
context.variant || variantandcontext.size || size. SinceVariantPropscan include falsy-but-defined values (e.g., a variant that maps to a default), nullish coalescing (??) would be more precise. In practice, variant/size are typically non-empty strings so||works here, but??better communicates intent.Proposed change
toggleVariants({ - variant: context.variant || variant, - size: context.size || size, + variant: context.variant ?? variant, + size: context.size ?? size, }),apps/mobile/components/ui/popover.tsx (1)
43-49: Web slide-in animations only covertopandbottomsides.The tooltip component (tooltip.tsx, lines 53-56) handles all four sides (
top,bottom,left,right) for web slide-in animations, but hereleftandrightare omitted. If a popover is positioned to the left or right, it will still fade/zoom in but won't have the directional slide.Add missing side animations for parity with tooltip
props.side === "bottom" && "slide-in-from-top-2", props.side === "top" && "slide-in-from-bottom-2", + props.side === "left" && "slide-in-from-right-2", + props.side === "right" && "slide-in-from-left-2",apps/mobile/screens/(authenticated)/demo/DemoScreen.tsx (2)
884-887: Invitation emails displayed in UI — verify this is demo-only.Line 886 renders
inv.emaildirectly. Per coding guidelines, be cautious about displaying PII like email addresses. Since this is a demo screen, it's likely acceptable, but ensure this pattern doesn't leak into production-facing screens.
97-923: Large single-component file — consider extracting demo sections if this grows further.At ~830 lines of JSX, the component is manageable for a demo showcase. If more components are added, consider splitting each Card demo into small extracted components (e.g.,
ButtonDemo,BadgeDemo) co-located in acomponents/subfolder under this screen's directory.
| <TextClassContext.Provider | ||
| value={cn( | ||
| "text-sm text-foreground", | ||
| variant === "destructive" && "text-destructive", | ||
| className, | ||
| )} |
There was a problem hiding this comment.
className leaks into TextClassContext, mixing View classes with text classes.
The Alert's className prop (intended for the outer View) is also spread into the TextClassContext.Provider value (Line 26). If a consumer passes layout classes like className="p-4 mt-2", those will be injected into all descendant text styling via the context, which is unintended.
Proposed fix
<TextClassContext.Provider
value={cn(
"text-sm text-foreground",
variant === "destructive" && "text-destructive",
- className,
)}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <TextClassContext.Provider | |
| value={cn( | |
| "text-sm text-foreground", | |
| variant === "destructive" && "text-destructive", | |
| className, | |
| )} | |
| <TextClassContext.Provider | |
| value={cn( | |
| "text-sm text-foreground", | |
| variant === "destructive" && "text-destructive", | |
| )} |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/alert.tsx` around lines 22 - 27, The provider value
for TextClassContext currently includes the Alert component's className (cn(...,
className)), which leaks outer View/layout classes into descendant text styling;
remove className from the TextClassContext.Provider value and instead keep only
text-related classes (e.g., cn("text-sm text-foreground", variant ===
"destructive" && "text-destructive")). If you need consumer control over text
classes, add an explicit prop like textClassName and include that in the
provider value; ensure the original className continues to be applied only to
the outer View/container.
| <NativeOnlyAnimatedView entering={FadeIn}> | ||
| <DropdownMenuPrimitive.SubContent | ||
| className={cn( | ||
| "bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | ||
| Platform.select({ | ||
| web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin) z-50 min-w-[8rem]", | ||
| }), | ||
| className, | ||
| )} | ||
| {...props} | ||
| /> | ||
| </NativeOnlyAnimatedView> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Copy-paste bug: references --radix-context-menu-* CSS variables instead of --radix-dropdown-menu-*.
Line 91 uses origin-(--radix-context-menu-content-transform-origin) — this should reference the dropdown-menu CSS variable. On web, Radix sets per-component CSS custom properties for positioning, so the dropdown menu content won't get the correct transform origin.
The same issue exists in DropdownMenuContent at line 138.
Proposed fix
Platform.select({
- web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin) z-50 min-w-[8rem]",
+ web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-dropdown-menu-content-transform-origin) z-50 min-w-[8rem]",
}),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <NativeOnlyAnimatedView entering={FadeIn}> | |
| <DropdownMenuPrimitive.SubContent | |
| className={cn( | |
| "bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin) z-50 min-w-[8rem]", | |
| }), | |
| className, | |
| )} | |
| {...props} | |
| /> | |
| </NativeOnlyAnimatedView> | |
| ); | |
| } | |
| <NativeOnlyAnimatedView entering={FadeIn}> | |
| <DropdownMenuPrimitive.SubContent | |
| className={cn( | |
| "bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-dropdown-menu-content-transform-origin) z-50 min-w-[8rem]", | |
| }), | |
| className, | |
| )} | |
| {...props} | |
| /> | |
| </NativeOnlyAnimatedView> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/dropdown-menu.tsx` around lines 86 - 99, The
className uses the wrong Radix CSS variable name causing incorrect transform
origin; in the web platform class strings for DropdownMenuPrimitive.SubContent
(inside NativeOnlyAnimatedView) and the sibling DropdownMenuContent component,
replace occurrences of "--radix-context-menu-content-transform-origin" with
"--radix-dropdown-menu-content-transform-origin" (i.e., update the origin-(...)
token) so the dropdown menu uses the correct per-component CSS custom property
for transform origin.
| <DropdownMenuPrimitive.Content | ||
| className={cn( | ||
| "bg-popover border-border min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | ||
| Platform.select({ | ||
| web: cn( | ||
| "animate-in fade-in-0 zoom-in-95 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) z-50 cursor-default", | ||
| props.side === "bottom" && "slide-in-from-top-2", | ||
| props.side === "top" && "slide-in-from-bottom-2", | ||
| ), | ||
| }), | ||
| className, | ||
| )} | ||
| {...props} |
There was a problem hiding this comment.
Same copy-paste bug: DropdownMenuContent also references context-menu CSS variables.
Both --radix-context-menu-content-available-height and --radix-context-menu-content-transform-origin should be --radix-dropdown-menu-*.
Proposed fix
web: cn(
- "animate-in fade-in-0 zoom-in-95 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) z-50 cursor-default",
+ "animate-in fade-in-0 zoom-in-95 max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) z-50 cursor-default",
props.side === "bottom" && "slide-in-from-top-2",
props.side === "top" && "slide-in-from-bottom-2",
),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <DropdownMenuPrimitive.Content | |
| className={cn( | |
| "bg-popover border-border min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: cn( | |
| "animate-in fade-in-0 zoom-in-95 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) z-50 cursor-default", | |
| props.side === "bottom" && "slide-in-from-top-2", | |
| props.side === "top" && "slide-in-from-bottom-2", | |
| ), | |
| }), | |
| className, | |
| )} | |
| {...props} | |
| <DropdownMenuPrimitive.Content | |
| className={cn( | |
| "bg-popover border-border min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: cn( | |
| "animate-in fade-in-0 zoom-in-95 max-h-(--radix-dropdown-menu-content-available-height) origin-(--radix-dropdown-menu-content-transform-origin) z-50 cursor-default", | |
| props.side === "bottom" && "slide-in-from-top-2", | |
| props.side === "top" && "slide-in-from-bottom-2", | |
| ), | |
| }), | |
| className, | |
| )} | |
| {...props} |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/dropdown-menu.tsx` around lines 133 - 145, The
DropdownMenuPrimitive.Content className uses context-menu CSS variables
incorrectly; update the CSS variable names inside the className string for
DropdownMenuPrimitive.Content (the block rendering DropdownMenuContent) by
replacing --radix-context-menu-content-available-height and
--radix-context-menu-content-transform-origin with their dropdown-menu
equivalents (--radix-dropdown-menu-content-available-height and
--radix-dropdown-menu-content-transform-origin) so the animation and max-height
origin use the correct variables while preserving the Platform.select logic and
existing props/className merging.
| /** | ||
| * A wrapper component for Lucide icons with Uniwind `className` support via `withUniwind`. | ||
| * | ||
| * This component allows you to render any Lucide icon while applying utility classes | ||
| * using `uniwind`. It avoids the need to wrap or configure each icon individually. | ||
| * | ||
| * @component | ||
| * @example | ||
| * ```tsx | ||
| * import { ArrowRight } from 'lucide-react-native'; | ||
| * import { Icon } from '@/registry/uniwind/registry/components/ui/icon'; | ||
| * | ||
| * <Icon as={ArrowRight} className="text-red-500 size-4" /> | ||
| * ``` |
There was a problem hiding this comment.
Incorrect import path in JSDoc example.
Line 34 references @/registry/uniwind/registry/components/ui/icon which doesn't match the actual module path. This will confuse consumers.
Fix the import path
- * import { Icon } from '@/registry/uniwind/registry/components/ui/icon';
+ * import { Icon } from '@/components/ui/icon';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * A wrapper component for Lucide icons with Uniwind `className` support via `withUniwind`. | |
| * | |
| * This component allows you to render any Lucide icon while applying utility classes | |
| * using `uniwind`. It avoids the need to wrap or configure each icon individually. | |
| * | |
| * @component | |
| * @example | |
| * ```tsx | |
| * import { ArrowRight } from 'lucide-react-native'; | |
| * import { Icon } from '@/registry/uniwind/registry/components/ui/icon'; | |
| * | |
| * <Icon as={ArrowRight} className="text-red-500 size-4" /> | |
| * ``` | |
| /** | |
| * A wrapper component for Lucide icons with Uniwind `className` support via `withUniwind`. | |
| * | |
| * This component allows you to render any Lucide icon while applying utility classes | |
| * using `uniwind`. It avoids the need to wrap or configure each icon individually. | |
| * | |
| * `@component` | |
| * `@example` | |
| * |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/icon.tsx` around lines 24 - 37, Update the JSDoc
example import path to match the actual module export for the Icon component:
locate the JSDoc block above the Icon component (search for "A wrapper component
for Lucide icons" and the Icon symbol) and change the example import from
"@/registry/uniwind/registry/components/ui/icon" to the correct module path that
exports Icon (the actual path used in the codebase where Icon is exported), so
the example imports the Icon component from the real module.
| import * as LabelPrimitive from "@rn-primitives/label"; | ||
| import { Platform } from "react-native"; | ||
| import { cn } from "@/lib/utils"; | ||
|
|
||
| function Label({ | ||
| className, | ||
| onPress, | ||
| onLongPress, | ||
| onPressIn, | ||
| onPressOut, | ||
| disabled, | ||
| ...props | ||
| }: LabelPrimitive.TextProps & React.RefAttributes<LabelPrimitive.TextRef>) { |
There was a problem hiding this comment.
Missing React import — same issue as separator.tsx.
React.RefAttributes is used on Line 13 without importing React. Add import * as React from "react" to match the convention used in other components.
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/label.tsx` around lines 1 - 13, The file uses
React.RefAttributes in the Label component signature but React is not imported;
add the missing import (import * as React from "react") at the top of the file
so React.RefAttributes and any other React types work correctly in the Label
function declaration and its props handling.
| return ( | ||
| <NativeOnlyAnimatedView entering={FadeIn}> | ||
| <MenubarPrimitive.SubContent | ||
| className={cn( | ||
| "bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | ||
| Platform.select({ | ||
| web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin) z-50 min-w-[8rem]", | ||
| }), | ||
| className, | ||
| )} | ||
| {...props} | ||
| /> | ||
| </NativeOnlyAnimatedView> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Same copy-paste bug: MenubarSubContent references --radix-context-menu-* CSS variables.
Line 161 uses origin-(--radix-context-menu-content-transform-origin). This should reference menubar-specific Radix CSS variables.
Proposed fix
Platform.select({
- web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin) z-50 min-w-[8rem]",
+ web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-menubar-content-transform-origin) z-50 min-w-[8rem]",
}),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return ( | |
| <NativeOnlyAnimatedView entering={FadeIn}> | |
| <MenubarPrimitive.SubContent | |
| className={cn( | |
| "bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-context-menu-content-transform-origin) z-50 min-w-[8rem]", | |
| }), | |
| className, | |
| )} | |
| {...props} | |
| /> | |
| </NativeOnlyAnimatedView> | |
| ); | |
| } | |
| return ( | |
| <NativeOnlyAnimatedView entering={FadeIn}> | |
| <MenubarPrimitive.SubContent | |
| className={cn( | |
| "bg-popover border-border overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: "animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 fade-in-0 data-[state=closed]:zoom-out-95 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-menubar-content-transform-origin) z-50 min-w-[8rem]", | |
| }), | |
| className, | |
| )} | |
| {...props} | |
| /> | |
| </NativeOnlyAnimatedView> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/menubar.tsx` around lines 155 - 169, The
MenubarSubContent component uses the wrong Radix CSS variable name; in the JSX
returned by MenubarPrimitive.SubContent inside NativeOnlyAnimatedView update the
origin utility that currently references
--radix-context-menu-content-transform-origin to use the menubar-specific
variable (e.g., --radix-menubar-content-transform-origin) so the
transform-origin matches the Menubar primitives; locate the className string
inside the MenubarPrimitive.SubContent render (the Platform.select web branch)
and replace the context-menu variable with the menubar variant.
| <MenubarPrimitive.Content | ||
| className={cn( | ||
| "bg-popover border-border min-w-[12rem] overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | ||
| Platform.select({ | ||
| web: cn( | ||
| "animate-in fade-in-0 zoom-in-95 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) z-50 cursor-default", | ||
| props.side === "bottom" && "slide-in-from-top-2", | ||
| props.side === "top" && "slide-in-from-bottom-2", | ||
| ), | ||
| }), | ||
| className, | ||
| )} | ||
| align={align} | ||
| alignOffset={alignOffset} | ||
| sideOffset={sideOffset} | ||
| {...props} | ||
| /> | ||
| </TextClassContext.Provider> | ||
| </NativeOnlyAnimatedView> | ||
| </FullWindowOverlay> | ||
| </MenubarPrimitive.Portal> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Radix UI menubar CSS custom properties documentation
💡 Result:
Radix UI Menubar’s CSS custom properties are documented on the Menubar (Primitives) docs page, in the Content (and SubContent) sections under the “CSS Variable” table. (radix-ui.com)
Menubar CSS custom properties (React Primitives): (radix-ui.com)
--radix-menubar-content-transform-origin--radix-menubar-content-available-width--radix-menubar-content-available-height--radix-menubar-trigger-width--radix-menubar-trigger-height
Sources (numbered):
[1] Radix Primitives — Menubar docs (radix-ui.com)
Citations:
- 1: https://www.radix-ui.com/primitives/docs/components/menubar?utm_source=openai
- 2: https://www.radix-ui.com/primitives/docs/components/menubar?utm_source=openai
- 3: https://www.radix-ui.com/primitives/docs/components/menubar?utm_source=openai
Replace --radix-context-menu-* CSS variables with --radix-menubar-* in MenubarContent.
Line 200 uses --radix-context-menu-content-available-height and --radix-context-menu-content-transform-origin, which are context menu variables. According to Radix UI documentation, menubar content should use --radix-menubar-content-available-height and --radix-menubar-content-transform-origin.
Proposed fix
web: cn(
- "animate-in fade-in-0 zoom-in-95 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) z-50 cursor-default",
+ "animate-in fade-in-0 zoom-in-95 max-h-(--radix-menubar-content-available-height) origin-(--radix-menubar-content-transform-origin) z-50 cursor-default",
props.side === "bottom" && "slide-in-from-top-2",
props.side === "top" && "slide-in-from-bottom-2",
),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <MenubarPrimitive.Content | |
| className={cn( | |
| "bg-popover border-border min-w-[12rem] overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: cn( | |
| "animate-in fade-in-0 zoom-in-95 max-h-(--radix-context-menu-content-available-height) origin-(--radix-context-menu-content-transform-origin) z-50 cursor-default", | |
| props.side === "bottom" && "slide-in-from-top-2", | |
| props.side === "top" && "slide-in-from-bottom-2", | |
| ), | |
| }), | |
| className, | |
| )} | |
| align={align} | |
| alignOffset={alignOffset} | |
| sideOffset={sideOffset} | |
| {...props} | |
| /> | |
| </TextClassContext.Provider> | |
| </NativeOnlyAnimatedView> | |
| </FullWindowOverlay> | |
| </MenubarPrimitive.Portal> | |
| ); | |
| } | |
| <MenubarPrimitive.Content | |
| className={cn( | |
| "bg-popover border-border min-w-[12rem] overflow-hidden rounded-md border p-1 shadow-lg shadow-black/5", | |
| Platform.select({ | |
| web: cn( | |
| "animate-in fade-in-0 zoom-in-95 max-h-(--radix-menubar-content-available-height) origin-(--radix-menubar-content-transform-origin) z-50 cursor-default", | |
| props.side === "bottom" && "slide-in-from-top-2", | |
| props.side === "top" && "slide-in-from-bottom-2", | |
| ), | |
| }), | |
| className, | |
| )} | |
| align={align} | |
| alignOffset={alignOffset} | |
| sideOffset={sideOffset} | |
| {...props} | |
| /> | |
| </TextClassContext.Provider> | |
| </NativeOnlyAnimatedView> | |
| </MenubarPrimitive.Portal> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/menubar.tsx` around lines 195 - 217, The Menubar
content CSS uses context-menu variables; update MenubarPrimitive.Content's
className to replace --radix-context-menu-content-available-height and
--radix-context-menu-content-transform-origin with the menubar equivalents
--radix-menubar-content-available-height and
--radix-menubar-content-transform-origin (keep the surrounding cn(...)
composition, Platform.select block, and the props.side conditional logic intact
so only the variable names change).
| function SelectItem({ | ||
| className, | ||
| children, | ||
| ...props | ||
| }: SelectPrimitive.ItemProps & React.RefAttributes<SelectPrimitive.ItemRef>) { | ||
| return ( | ||
| <SelectPrimitive.Item | ||
| className={cn( | ||
| "active:bg-accent group relative flex w-full flex-row items-center gap-2 rounded-sm py-2 pl-2 pr-8 sm:py-1.5", | ||
| Platform.select({ | ||
| web: "focus:bg-accent focus:text-accent-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 cursor-default outline-none data-[disabled]:pointer-events-none [&_svg]:pointer-events-none", | ||
| }), | ||
| props.disabled && "opacity-50", | ||
| className, | ||
| )} | ||
| {...props} | ||
| > | ||
| <View className="absolute right-2 flex size-3.5 items-center justify-center"> | ||
| <SelectPrimitive.ItemIndicator> | ||
| <Icon as={Check} className="text-muted-foreground size-4 shrink-0" /> | ||
| </SelectPrimitive.ItemIndicator> | ||
| </View> | ||
| <SelectPrimitive.ItemText className="text-foreground group-active:text-accent-foreground select-none text-sm" /> | ||
| </SelectPrimitive.Item> | ||
| ); | ||
| } |
There was a problem hiding this comment.
children prop is accepted but silently discarded.
SelectItem destructures children from props (line 172) but never renders them. Only SelectPrimitive.ItemText is rendered. In the DemoScreen (lines 405–413), children like Apple are passed but silently ignored — the label prop drives the display.
Either remove children from the destructured props to make the API clearer, or render them if intended.
Option A: Stop accepting children
function SelectItem({
className,
- children,
...props
}: SelectPrimitive.ItemProps & React.RefAttributes<SelectPrimitive.ItemRef>) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function SelectItem({ | |
| className, | |
| children, | |
| ...props | |
| }: SelectPrimitive.ItemProps & React.RefAttributes<SelectPrimitive.ItemRef>) { | |
| return ( | |
| <SelectPrimitive.Item | |
| className={cn( | |
| "active:bg-accent group relative flex w-full flex-row items-center gap-2 rounded-sm py-2 pl-2 pr-8 sm:py-1.5", | |
| Platform.select({ | |
| web: "focus:bg-accent focus:text-accent-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 cursor-default outline-none data-[disabled]:pointer-events-none [&_svg]:pointer-events-none", | |
| }), | |
| props.disabled && "opacity-50", | |
| className, | |
| )} | |
| {...props} | |
| > | |
| <View className="absolute right-2 flex size-3.5 items-center justify-center"> | |
| <SelectPrimitive.ItemIndicator> | |
| <Icon as={Check} className="text-muted-foreground size-4 shrink-0" /> | |
| </SelectPrimitive.ItemIndicator> | |
| </View> | |
| <SelectPrimitive.ItemText className="text-foreground group-active:text-accent-foreground select-none text-sm" /> | |
| </SelectPrimitive.Item> | |
| ); | |
| } | |
| function SelectItem({ | |
| className, | |
| ...props | |
| }: SelectPrimitive.ItemProps & React.RefAttributes<SelectPrimitive.ItemRef>) { | |
| return ( | |
| <SelectPrimitive.Item | |
| className={cn( | |
| "active:bg-accent group relative flex w-full flex-row items-center gap-2 rounded-sm py-2 pl-2 pr-8 sm:py-1.5", | |
| Platform.select({ | |
| web: "focus:bg-accent focus:text-accent-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 cursor-default outline-none data-[disabled]:pointer-events-none [&_svg]:pointer-events-none", | |
| }), | |
| props.disabled && "opacity-50", | |
| className, | |
| )} | |
| {...props} | |
| > | |
| <View className="absolute right-2 flex size-3.5 items-center justify-center"> | |
| <SelectPrimitive.ItemIndicator> | |
| <Icon as={Check} className="text-muted-foreground size-4 shrink-0" /> | |
| </SelectPrimitive.ItemIndicator> | |
| </View> | |
| <SelectPrimitive.ItemText className="text-foreground group-active:text-accent-foreground select-none text-sm" /> | |
| </SelectPrimitive.Item> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/select.tsx` around lines 169 - 194, SelectItem
currently destructures children but never renders them, so labels like "Apple"
passed from DemoScreen are ignored; fix by rendering children inside
SelectPrimitive.ItemText (replace the self-closing <SelectPrimitive.ItemText ...
/> with <SelectPrimitive.ItemText className="..."
>{children}</SelectPrimitive.ItemText>) so the passed children are displayed;
alternatively, if children should not be accepted, remove children from the
SelectItem parameter list and its type (SelectPrimitive.ItemProps &
React.RefAttributes<SelectPrimitive.ItemRef>) to stop accepting them.
| }: SeparatorPrimitive.RootProps & | ||
| React.RefAttributes<SeparatorPrimitive.RootRef>) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, view the actual separator.tsx file
echo "=== separator.tsx content ==="
cat -n apps/mobile/components/ui/separator.tsx
echo -e "\n=== Check for React imports in similar files ==="
# Check alert.tsx
echo "--- alert.tsx ---"
head -15 apps/mobile/components/ui/alert.tsx
# Check badge.tsx
echo -e "\n--- badge.tsx ---"
head -15 apps/mobile/components/ui/badge.tsxRepository: superset-sh/superset
Length of output: 2051
🏁 Script executed:
#!/bin/bash
# Check tsconfig for JSX and type settings
echo "=== tsconfig configuration ==="
fd tsconfig -e json --exec cat {} \;
echo -e "\n=== Check for ambient React declarations ==="
rg "declare global" -A5 --type=ts
echo -e "\n=== Check if React is globally available ==="
rg "type React" --type=ts -l | head -5Repository: superset-sh/superset
Length of output: 6698
🏁 Script executed:
#!/bin/bash
# Check package.json to understand React version and setup
echo "=== React and TypeScript configuration ==="
cat package.json | grep -A2 -B2 '"react"'
# Look for .d.ts files that might provide global types
echo -e "\n=== Global type definition files ==="
fd -e d.ts -x ls -la {}Repository: superset-sh/superset
Length of output: 1007
Missing React import for type annotation.
React.RefAttributes is referenced in the type annotation on line 10 but React is not imported. While the new JSX transform (react-jsx) eliminates the need to import React for JSX elements, it does not apply to type references like React.RefAttributes. This will cause a TypeScript compilation error.
Proposed fix
import * as SeparatorPrimitive from "@rn-primitives/separator";
+import * as React from "react";
import { cn } from "@/lib/utils";🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/separator.tsx` around lines 9 - 10, Add a React
type import so the type reference React.RefAttributes compiles: update the
imports at the top of the separator component file to import React for types
(e.g., import * as React from "react" or import React from "react") so the
annotation SeparatorPrimitive.RootProps &
React.RefAttributes<SeparatorPrimitive.RootRef> is valid; ensure the import is
present alongside any existing SeparatorPrimitive imports in
components/ui/separator.tsx.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@apps/mobile/components/ui/textarea.tsx`:
- Around line 4-10: The component Textarea declares
React.RefAttributes<TextInput> in its props but doesn't use React.forwardRef, so
incoming refs are dropped; wrap Textarea with React.forwardRef (use
React.forwardRef<TextInput, TextInputProps>(...)) and change the function to
accept (props, ref) then pass that ref to the underlying TextInput element,
ensuring the ref type matches TextInput and update any export to use the
forwarded component.
🧹 Nitpick comments (1)
apps/mobile/components/ui/textarea.tsx (1)
27-28:{...props}spread aftertextAlignVerticalallows silent override.Because
{...props}is spread aftertextAlignVertical="top", a caller passingtextAlignVerticalwill override the default. If this is intentional for flexibility, consider adding a brief comment. If the default should always win, swap the order or destructuretextAlignVerticallikemultiline.
| function Textarea({ | ||
| className, | ||
| multiline = true, | ||
| numberOfLines = Platform.select({ web: 2, native: 8 }), // On web, numberOfLines also determines initial height. On native, it determines the maximum height. | ||
| placeholderTextColorClassName, | ||
| ...props | ||
| }: TextInputProps & React.RefAttributes<TextInput>) { |
There was a problem hiding this comment.
Ref will be silently dropped — forwardRef is missing.
The props type includes React.RefAttributes<TextInput>, which advertises that <Textarea ref={…} /> is valid. However, without wrapping in React.forwardRef, React strips the ref before it reaches the function body, so it never arrives on the underlying <TextInput>.
Proposed fix
-function Textarea({
+import * as React from "react";
+
+const Textarea = React.forwardRef<TextInput, TextInputProps & { placeholderTextColorClassName?: string }>(({
className,
multiline = true,
numberOfLines = Platform.select({ web: 2, native: 8 }),
placeholderTextColorClassName,
...props
-}: TextInputProps & React.RefAttributes<TextInput>) {
+}, ref) => {
return (
<TextInput
+ ref={ref}
className={cn(
...
)}
...
/>
);
-}
+});
+
+Textarea.displayName = "Textarea";🤖 Prompt for AI Agents
In `@apps/mobile/components/ui/textarea.tsx` around lines 4 - 10, The component
Textarea declares React.RefAttributes<TextInput> in its props but doesn't use
React.forwardRef, so incoming refs are dropped; wrap Textarea with
React.forwardRef (use React.forwardRef<TextInput, TextInputProps>(...)) and
change the function to accept (props, ref) then pass that ref to the underlying
TextInput element, ensuring the ref type matches TextInput and update any export
to use the forwarded component.
Summary
.mcp.jsonfor Expo documentation access<>fragments, added missing dependency touseEffect)Test plan
bun run test— 0 failures)Summary by CodeRabbit
New Features
Chores