refactor(deps): all in shadcn-ui#136
Conversation
Upgrade HeroUI and related UI dependencies to the new major API surface. Migrate existing tool pages to HeroUI v3 compound components, update resizable panel orientation props, and adapt animate-ui motion wrappers to current types.
- Added unoptimized images configuration in next.config.ts. - Replaced inline script with next/script component in _layout.tsx for improved loading strategy.
- Removed dependencies for @heroui/react and @heroui/styles from package.json and pnpm-lock.yaml. - Added shadcn as a new dependency. - Updated imports in various components to use shadcn instead of HeroUI components. - Adjusted ResizablePanel properties for consistency across components.
❌ Deploy Preview for tools-aiwan-run failed.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughUpdates package metadata and performs a large dependency and devDependency overhaul in package.json: bumps pnpm, removes HeroUI/radix packages, adds Base UI and several UI/chart libraries, and upgrades many tooling/runtime versions. ChangesConfiguration & Dependencies
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 15
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/(locale)/[locale]/pinyin/page.tsx (1)
21-78:⚠️ Potential issue | 🟠 Major
aria-invalidchecks are i18n-broken and one targets the wrong field.Two issues:
aria-invalid={inputText === 'Invalid input'}(line 53) compares the user's raw input to the literal sentinel — the input field is only “invalid” if pinyin parsing failed, which is reflected in the output, not ininputText. This effectively never marks the input as invalid (and would falsely mark it if a user typed “Invalid input”).- Both lines 53 and 73 string-compare against the literal
'Invalid input', butpingyinTextis set viat\Invalid input`(line 28), which returns a *translated* string at runtime. In any non-English locale these comparisons are alwaysfalse`, silently breaking the invalid state.Track invalidity with an explicit boolean instead of comparing translated strings.
🐛 Proposed fix
- const pingyinText = useMemo(() => { - let newPingyinText = '' - try { - newPingyinText = pinyin(inputText, { - style: isShowTone ? pinyin.STYLE_TONE : pinyin.STYLE_NORMAL, - }).join(' ') - } catch { - newPingyinText = t`Invalid input` - } - return newPingyinText - }, [inputText, isShowTone, t]) + const { pingyinText, isInvalid } = useMemo(() => { + try { + return { + pingyinText: pinyin(inputText, { + style: isShowTone ? pinyin.STYLE_TONE : pinyin.STYLE_NORMAL, + }).join(' '), + isInvalid: false, + } + } catch { + return { pingyinText: t`Invalid input`, isInvalid: true } + } + }, [inputText, isShowTone, t]) @@ - aria-invalid={inputText === 'Invalid input'} + aria-invalid={isInvalid} @@ - aria-invalid={pingyinText === 'Invalid input'} + aria-invalid={isInvalid}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(locale)/[locale]/pinyin/page.tsx around lines 21 - 78, The pinyin invalid check must stop comparing translated strings and use an explicit boolean: add a new state/variable (e.g., pinyinInvalid or isPinyinInvalid) and set it when the pinyin conversion in the useMemo for pingyinText throws (set true in catch, false on success); keep the displayed error text as t`Invalid input` but use the boolean for aria-invalid on the input Textarea (replace aria-invalid={inputText === 'Invalid input'}) and on the output Textarea (replace aria-invalid={pingyinText === 'Invalid input'}), and ensure any handlers that clear input (setInputText) also clear the invalid flag as appropriate.
🟠 Major comments (9)
src/components/ui/drawer.tsx-40-40 (1)
40-40:⚠️ Potential issue | 🟠 MajorOverlay fade animations likely won't fire —
data-open:/data-closed:don't match vaul'sdata-stateattribute.In Tailwind v4, the boolean data-attribute variants
data-open:anddata-closed:compile to attribute-presence selectors[data-open]/[data-closed]. However, vaul'sDrawer.Overlaytoggles its visibility viadata-state="open"/data-state="closed"(not adata-openboolean attribute). As a result,animate-in fade-in-0andanimate-out fade-out-0will never apply, and the overlay won't fade in/out as intended. This also diverges from upstream shadcn-ui's drawer, which usesdata-[state=open]:/data-[state=closed]:.🐛 Proposed fix
data-slot="drawer-overlay" className={cn( - "fixed inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", + "fixed inset-0 z-50 bg-black/10 supports-backdrop-filter:backdrop-blur-xs data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=closed]:animate-out data-[state=closed]:fade-out-0", className )}If this project defines a custom Tailwind variant mapping
data-open→[data-state=open](or vaul has been customized to emitdata-open/data-closed), this is a non-issue. Please confirm with:#!/bin/bash # 1) Look for custom `@custom-variant` / `@variant` definitions in CSS for data-open / data-closed rg -nP -C2 '@(custom-)?variant\s+data-(open|closed)\b' --type=css # 2) Confirm what data-* attributes vaul renders on Overlay/Content in the version in use fd -t f 'package.json' -x grep -lE '"vaul"\s*:' {} \; | head -n 5 rg -nP -C2 '"vaul"\s*:' package.json pnpm-lock.yaml yarn.lock 2>/dev/null | head -n 50 # 3) Cross-check usages elsewhere in this PR's UI primitives for the same pattern rg -nP --type=tsx -C1 '\bdata-(open|closed):'In Tailwind CSS v4, does the `data-open:` variant compile to `[data-open]` (attribute presence) or to `[data-state=open]`? And what data-state attribute does `vaul` 1.1.2's Drawer.Overlay set when open/closed?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/drawer.tsx` at line 40, The overlay's Tailwind variants use data-open:/data-closed: which target boolean attributes and won't match vaul's data-state="open"/"closed"; update the class string on Drawer.Overlay (the quoted class value in src/components/ui/drawer.tsx) to use data-[state=open]:animate-in data-[state=open]:fade-in-0 and data-[state=closed]:animate-out data-[state=closed]:fade-out-0 (i.e., replace data-open:/data-closed: with data-[state=open]:/data-[state=closed]:) so the fade animations apply when vaul toggles data-state, and re-run the build to verify Tailwind compiles these arbitrary attribute variants.src/components/ui/slider.tsx-13-17 (1)
13-17:⚠️ Potential issue | 🟠 MajorHandle scalar value/defaultValue props to match component's type signature.
The component accepts
SliderPrimitive.Root.Props, which includesnumber | number[]forvalueanddefaultValue. However, the_valuescalculation (lines 13–17) only checksArray.isArray(), silently ignoring scalar numbers. If a developer passesdefaultValue={50}, it falls through to the[min, max]fallback, rendering two thumbs instead of one.Additionally, when neither value nor defaultValue is provided, defaulting to
[min, max]creates a two-thumb slider by default, which may not match single-value use cases.Wrap scalar values as arrays and consider defaulting to
[min]for single-thumb sliders:Suggested fix
- const _values = Array.isArray(value) - ? value - : Array.isArray(defaultValue) - ? defaultValue - : [min, max] + const _values = Array.isArray(value) + ? value + : Array.isArray(defaultValue) + ? defaultValue + : typeof value === "number" + ? [value] + : typeof defaultValue === "number" + ? [defaultValue] + : [min]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/slider.tsx` around lines 13 - 17, _value computation currently ignores scalar number props causing unintended two-thumb sliders; update the logic that builds _values (used by the Slider component) to: treat a numeric value or defaultValue as a single-thumb by wrapping it into an array (e.g., if typeof value === 'number' then _values = [value]), preserve arrays as-is, and when neither value nor defaultValue is provided default to a single-thumb baseline (e.g., [min]) instead of [min, max]; reference the _values variable and the value/defaultValue props from SliderPrimitive.Root.Props and ensure min/max remain available for bounds.src/components/ui/field.tsx-118-129 (1)
118-129:⚠️ Potential issue | 🟠 Major
FieldTitleuses the wrongdata-slotvalue.
FieldTitleis set todata-slot="field-label", which collides withFieldLabel(line 107). Any CSS selector orhas-[[data-slot=field-label]]:rule (and there are several in this file) will match titles too, leading to incorrect styling.🐛 Proposed fix
<div - data-slot="field-label" + data-slot="field-title" className={cn(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/field.tsx` around lines 118 - 129, FieldTitle is using the same data-slot value as FieldLabel causing selector collisions; update the FieldTitle component (function FieldTitle) to use a distinct data-slot value such as "field-title" instead of "field-label", and then scan this file for any CSS/has-[[data-slot=field-label]] rules that should target labels vs titles and adjust selectors accordingly so only FieldLabel matches "field-label" and FieldTitle matches "field-title".src/components/animate-ui/primitives/buttons/ripple.tsx-43-65 (1)
43-65:⚠️ Potential issue | 🟠 MajorRipple
idcollisions on rapid clicks, and the cleanup timer leaks past unmount.Two concrete issues:
id: Date.now()is not unique within the same millisecond — fast double clicks (or replayed taps) produce identical keys, causing Reactkeywarnings and the filter on line 61 deletes both ripples at once.- The
setTimeoutis never cleared. If the component unmounts before 600 ms elapse,setRipplesruns on an unmounted instance.🐛 Proposed fix
+ const timeoutsRef = React.useRef<Set<ReturnType<typeof setTimeout>>>(new Set()); + + React.useEffect(() => { + return () => { + timeoutsRef.current.forEach(clearTimeout); + timeoutsRef.current.clear(); + }; + }, []); + const createRipple = React.useCallback( (event: React.MouseEvent<HTMLButtonElement>) => { const button = buttonRef.current; if (!button) return; const rect = button.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const newRipple: Ripple = { - id: Date.now(), + // Combine timestamp with a random suffix to avoid collisions on rapid clicks. + id: Date.now() + Math.random(), x, y, }; setRipples((prev) => [...prev, newRipple]); - setTimeout(() => { + const timeout = setTimeout(() => { setRipples((prev) => prev.filter((r) => r.id !== newRipple.id)); + timeoutsRef.current.delete(timeout); }, 600); + timeoutsRef.current.add(timeout); }, [], );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/animate-ui/primitives/buttons/ripple.tsx` around lines 43 - 65, The ripple ids generated in createRipple using Date.now() can collide and the setTimeout used to remove ripples is never cleared on unmount; replace the id generation with a truly unique id (e.g., a per-component counter ref or crypto.randomUUID()) so each newRipple.id is unique, and capture timeout handles (store them in a ref like rippleTimeoutsRef) when calling setTimeout so you can clear them on unmount; add a useEffect cleanup that iterates rippleTimeoutsRef to clearTimeout every pending timer and optionally clear the ref, and update createRipple to push the timeout handle into that ref when scheduling the removal for setRipples.src/components/animate-ui/primitives/animate/slot.tsx-48-69 (1)
48-69:⚠️ Potential issue | 🟠 MajorEvent handlers from the child are silently overwritten.
mergePropsdoes{ ...childProps, ...slotProps }, so any handler defined on the consumer's child element (e.g.onClick,onPointerDown) is replaced by the slot's handler rather than chained. This diverges from the Radix/shadcnSlotcontract and affectsRippleButtonwhere the slot forwardsonClick={handleClick}— any user-suppliedonClickon the child element would be lost.Consider chaining functions for known event props (or any prop starting with
onwhose value is a function), preserving className/style merge as-is.♻️ Suggested approach
function mergeProps<T extends HTMLElement>( childProps: AnyProps, slotProps: DOMMotionProps<T>, ): AnyProps { const merged: AnyProps = { ...childProps, ...slotProps }; + // Chain event handlers (props starting with "on") so child handlers still fire. + for (const key of Object.keys(slotProps)) { + const slotValue = (slotProps as AnyProps)[key]; + const childValue = childProps[key]; + if ( + key.startsWith("on") && + typeof slotValue === "function" && + typeof childValue === "function" + ) { + merged[key] = (...args: unknown[]) => { + (childValue as (...a: unknown[]) => unknown)(...args); + (slotValue as (...a: unknown[]) => unknown)(...args); + }; + } + } + if (childProps.className || slotProps.className) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/animate-ui/primitives/animate/slot.tsx` around lines 48 - 69, mergeProps currently spreads slotProps over childProps which silently overwrites event handlers; update mergeProps (the function named mergeProps<T extends HTMLElement>) to detect prop keys starting with "on" whose values are functions in both childProps and slotProps and replace the simple overwrite with a chained function that calls both handlers (preserve event object and call child handler first then slot handler, or otherwise maintain existing order used by Radix/shadcn), while keeping the existing className merging via cn and style merging logic intact; ensure you handle cases where only one side has the handler and preserve typings for DOMMotionProps<T>/AnyProps.src/components/ui/button-group.tsx-1-7 (1)
1-7:⚠️ Potential issue | 🟠 MajorMissing
"use client"directive.
ButtonGroupTextcallsuseRender(...)from@base-ui/react/use-render, which is a React hook and uses hooks internally. In Next.js App Router this file must be a Client Component, otherwise rendering it from a Server Component will throw at runtime. Every other base-ui-consuming file added in this PR (avatar.tsx,context-menu.tsx,hover-card.tsx,menubar.tsx,tabs.tsx) declares"use client".🛠 Proposed fix
+"use client" + import { mergeProps } from "@base-ui/react/merge-props" import { useRender } from "@base-ui/react/use-render" import { cva, type VariantProps } from "class-variance-authority"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/button-group.tsx` around lines 1 - 7, This file is missing the "use client" directive so the React hook useRender used in ButtonGroupText will break when rendered from a Server Component; add the top-of-file directive "use client" (a single line string) before any imports in src/components/ui/button-group.tsx to mark this as a Client Component so ButtonGroupText and its useRender hook can run safely.src/components/show-case.tsx-110-142 (1)
110-142:⚠️ Potential issue | 🟠 MajorSSR/SEO regression: actual card content is no longer present in server-rendered HTML.
useHydratedreturnsfalseduring SSR and the initial client render, so the server-rendered output now contains onlySkeletonplaceholders — the tool titles, descriptions, and links are gone from the initial HTML. The previousSkeleton isLoadedwrapper rendered the real content underneath the skeleton, so it was indexable and visible to no-JS users.If the goal of the gating is just to avoid hydration mismatches from animation state, consider rendering the card content unconditionally and only deferring the
motionanimations / dynamic bits, or accepting the briefmotionrender on the server. As-is, the showcase grid is not crawlable by no-JS clients.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/show-case.tsx` around lines 110 - 142, The server render currently shows only Skeletons because useHydrated gating in the ShowCase component hides the real card markup; change the logic so the actual card content (CardHeader, CardContent, Link, img, titles/descriptions) is rendered unconditionally on server and client, and only gate animation/dynamic behavior (e.g., motion components or animation classes tied to useHydrated) behind useHydrated; locate the conditional around isHydrated in src/components/show-case.tsx and replace it so Skeletons are used as overlays/placeholders or for client-only animated transitions while the real content (item.title, item.description, item.href, item.icon, CardHeader/CardContent) remains in the DOM for SSR/SEO.src/hooks/use-controlled-state.tsx-8-31 (1)
8-31:⚠️ Potential issue | 🟠 MajorFix type safety and replace effect-based sync with derived state.
Two related issues:
Type safety violation:
Rest extends any[]at line 8 usesany[]. Replace withunknown[](rest-tuple constraint) to maintain type safety—call sites that pass extra args remain typed.useEffect anti-pattern: Syncing
valueinto local state viauseEffect(lines 19–21) causes an extra render on every controlled prop change and is fragile when transitioning between controlled and uncontrolled modes. The idiomatic React approach is to derive the rendered value during render: usestate = value !== undefined ? value : internalStateand only updateinternalStatewhen uncontrolled. This eliminates the extra commit and stale state risks.Proposed refactor
export function useControlledState<T, Rest extends any[] = []>( +export function useControlledState<T, Rest extends unknown[] = []>( props: CommonControlledStateProps<T> & { onChange?: (value: T, ...args: Rest) => void }, ): readonly [T, (next: T, ...args: Rest) => void] { const { value, defaultValue, onChange } = props - const [state, setInternalState] = React.useState<T>( + const [internalState, setInternalState] = React.useState<T>( value !== undefined ? value : (defaultValue as T), ) - - React.useEffect(() => { - if (value !== undefined) setInternalState(value) - }, [value]) + // Derive state during render; prefer controlled prop when provided. + const isControlled = value !== undefined + const state = isControlled ? (value as T) : internalState const setState = React.useCallback( (next: T, ...args: Rest) => { - setInternalState(next) + if (!isControlled) setInternalState(next) onChange?.(next, ...args) }, - [onChange], + [isControlled, onChange], ) return [state, setState] as const }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/use-controlled-state.tsx` around lines 8 - 31, In useControlledState, replace the unsafe Rest extends any[] with Rest extends unknown[] to preserve tuple typing; remove the useEffect sync of value into internal state and instead compute the exposed state as const state = value !== undefined ? value : internalState during render, and only call setInternalState when the component is uncontrolled (i.e., when value is undefined) inside the setState callback (keep calling onChange(next, ...args) regardless); update references to value, defaultValue, internalState, setInternalState, setState and onChange in useControlledState accordingly to ensure no extra effect-driven renders and correct types.src/components/ui/sidebar.tsx-484-484 (1)
484-484:⚠️ Potential issue | 🟠 MajorRemove
hsl()wrapper from--sidebar-borderand--sidebar-accentvariables.Line 484 wraps full OKLCH color values with
hsl(var(...)), producing invalid CSS syntax likehsl(oklch(...)). The shadow will silently fail to render.Your
globals.cssdefines--sidebar-borderand--sidebar-accentas complete color values (e.g.,oklch(0.92 0.004 286.32)), not bare HSL channels. Usevar(--sidebar-border)andvar(--sidebar-accent)directly instead:Line 484 before and after
- "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]", + "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/sidebar.tsx` at line 484, The CSS class string in the sidebar component is wrapping CSS custom properties with hsl(), producing invalid output like hsl(oklch(...)); update the class value used in the Sidebar component (the array/string that contains "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]") to remove the hsl(...) wrappers and use var(--sidebar-border) and var(--sidebar-accent) directly so shadows become "shadow-[0_0_0_1px_var(--sidebar-border)]" and "hover:shadow-[0_0_0_1px_var(--sidebar-accent)]".
🟡 Minor comments (14)
src/components/ui/navigation-menu.tsx-116-116 (1)
116-116:⚠️ Potential issue | 🟡 Minor
easing-[ease]is not a Tailwind utility.The transition-timing-function utility is
ease-*(e.g.ease-[ease]/ease-linear).easing-[ease]will be emitted as an unknown class and stripped. Either drop it (theease-[cubic-bezier(0.22,1,0.36,1)]later in the same string already sets the timing function) or replace withease-[ease].🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/navigation-menu.tsx` at line 116, The class string on NavigationMenuPrimitive.Popup contains a non-existent Tailwind utility "easing-[ease]" which will be stripped; update the className on NavigationMenuPrimitive.Popup to either remove "easing-[ease]" entirely (since you already have ease-[cubic-bezier(0.22,1,0.36,1)]) or replace it with the correct Tailwind utility "ease-[ease]" so the timing function is applied correctly.src/components/ui/kbd.tsx-16-24 (1)
16-24:⚠️ Potential issue | 🟡 Minor
KbdGroupprops type doesn't match the rendered element.
KbdGroupis typed asReact.ComponentProps<"div">but renders a<kbd>element. Either change the rendered element to<div>or change the props type toReact.ComponentProps<"kbd">so the type contract matches what consumers can actually pass.🛠️ Suggested fix
-function KbdGroup({ className, ...props }: React.ComponentProps<"div">) { +function KbdGroup({ className, ...props }: React.ComponentProps<"kbd">) { return ( <kbd data-slot="kbd-group" className={cn("inline-flex items-center gap-1", className)} {...props} /> ) }As per coding guidelines: "Ensure implementations are type-safe (no
any), use readable variable/function names".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/kbd.tsx` around lines 16 - 24, KbdGroup is typed as React.ComponentProps<"div"> but renders a <kbd>, creating a type mismatch; update the props type on the KbdGroup component to React.ComponentProps<"kbd"> (or alternatively change the rendered element to a <div>) so the declared prop type matches the actual rendered element, and ensure the component signature (KbdGroup) and its usage still accept className and spread props correctly.src/components/ui/sonner.tsx-39-43 (1)
39-43:⚠️ Potential issue | 🟡 MinorDefine the
cn-toastCSS class in globals.css or replace with an existing style.The
toast: "cn-toast"class at line 41 is not defined anywhere in the codebase. Either add a.cn-toastrule toglobals.css, or use an existing Tailwind class instead. Without the definition, this className has no effect on toast styling.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/sonner.tsx` around lines 39 - 43, The toast configuration in src/components/ui/sonner.tsx sets toastOptions.classNames.toast = "cn-toast", but that CSS class isn't defined; either add a .cn-toast rule to your global stylesheet (e.g., globals.css) with the desired styles for the toast, or replace "cn-toast" with an existing Tailwind utility/class (or component class) used elsewhere; update the export in sonner.tsx to reference the chosen class name and ensure the CSS entry targets the toast element (search for toastOptions, classNames, and "cn-toast" to locate the code to change).src/components/ui/carousel.tsx-96-105 (1)
96-105:⚠️ Potential issue | 🟡 MinorDetach the
reInitlistener in cleanup.The effect subscribes to both
reInitandselect, but onlyselectis removed on cleanup. If the effect re-runs (e.g., a newonSelectreference) the priorreInithandler stays attached to the same Emblaapi, accumulating listeners.♻️ Proposed fix
return () => { api?.off("select", onSelect) + api?.off("reInit", onSelect) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/carousel.tsx` around lines 96 - 105, The effect attaches both the "reInit" and "select" listeners to the Embla `api` but only removes "select" on cleanup, causing leaked handlers when `onSelect` or `api` change; update the cleanup to remove both listeners (call api.off("reInit", onSelect) and api.off("select", onSelect)) so that `useEffect` which references `api` and `onSelect` properly detaches both handlers (refer to the existing React.useEffect, `api`, and `onSelect` symbols).src/components/animate-ui/components/buttons/ripple.tsx-27-42 (1)
27-42:⚠️ Potential issue | 🟡 Minor
variantmay beundefined, and the keyof cast hides missing entries.When the consumer omits
variant,buttonVariantsfalls back to its'default'defaultVariant for styling, butrippleButtonVariants[undefined as never]returnsundefined, so the--ripple-button-ripple-colorCSS variable is never set and the ripple uses the fallback (or stays unstyled). Additionally,variant as keyof typeof rippleButtonVariantswill type-check even if a new variant is added tobuttonVariantswithout a matching ripple-color entry, leading to silent regressions.♻️ Proposed fix
function RippleButton({ className, - variant, + variant = 'default', size, ...props }: RippleButtonProps) { return ( <RippleButtonPrimitive className={cn( buttonVariants({ variant, size, className }), - rippleButtonVariants[variant as keyof typeof rippleButtonVariants], + variant ? rippleButtonVariants[variant] : rippleButtonVariants.default, )} {...props} /> ); }Better still, type
rippleButtonVariantsasRecord<NonNullable<VariantProps<typeof buttonVariants>['variant']>, string>so the compiler enforces parity withbuttonVariants.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/animate-ui/components/buttons/ripple.tsx` around lines 27 - 42, The ripple color lookup can be undefined because RippleButton reads rippleButtonVariants[variant as keyof ...] while variant may be undefined; update RippleButton to guard/derive the effectiveVariant (use the same default as buttonVariants when variant is missing) and use that to index rippleButtonVariants instead of casting, and change the rippleButtonVariants type to enforce parity with buttonVariants by declaring it as Record<NonNullable<VariantProps<typeof buttonVariants>['variant']>, string> so the compiler forces matching keys and prevents silent regressions (adjust any code that constructs rippleButtonVariants to satisfy the new type).src/components/ui/empty.tsx-71-82 (1)
71-82:⚠️ Potential issue | 🟡 Minor
EmptyDescriptiontyped as<p>but renders<div>.The prop type is
React.ComponentProps<"p">(sorefis typedRef<HTMLParagraphElement>andpropsare paragraph-flavored), but the rendered element is a<div>. This is a type/runtime mismatch — refs will be wrong, and a description is semantically a paragraph anyway.🛠 Proposed fix (render a `
`)
function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { return ( - <div + <p data-slot="empty-description" className={cn( "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", className )} {...props} /> ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/empty.tsx` around lines 71 - 82, EmptyDescription is typed as React.ComponentProps<"p"> but currently returns a <div>, causing mismatched ref/prop types and semantics; update the component to render a <p> element instead of a <div> (keep the data-slot="empty-description", preserve the cn(className...) value and spread {...props}) so the HTML element matches the declared React.ComponentProps<"p"> and paragraph semantics remain correct.src/components/ui/context-menu.tsx-142-153 (1)
142-153:⚠️ Potential issue | 🟡 Minor
classNameis dropped when a consumer passes one toContextMenuSubContent.
classNameis not destructured here, so when a consumer passes one, the trailing{...props}spread overrides the literalclassName="shadow-lg"and the submenu loses its distinguishing shadow. The siblingMenubarSubContent(src/components/ui/menubar.tsxlines 250–261) correctly merges viacn().🛠 Proposed fix
function ContextMenuSubContent({ + className, ...props }: React.ComponentProps<typeof ContextMenuContent>) { return ( <ContextMenuContent data-slot="context-menu-sub-content" - className="shadow-lg" + className={cn("shadow-lg", className)} side="right" {...props} /> ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/context-menu.tsx` around lines 142 - 153, ContextMenuSubContent currently drops a consumer-provided className because props are spread after the literal className; fix by destructuring className from React.ComponentProps<typeof ContextMenuContent> in the ContextMenuSubContent signature, import/use the shared cn() utility (same pattern as MenubarSubContent), and pass className={cn("shadow-lg", className)} into the ContextMenuContent so consumer classes are merged rather than overriding the shadow.src/components/ui/alert-dialog.tsx-144-155 (1)
144-155:⚠️ Potential issue | 🟡 Minor
AlertDialogActiondoes not auto-close the dialog on click.
AlertDialogActionrenders a plainButtonwith noAlertDialogPrimitive.Closewrapper, preventing automatic dialog dismissal. In contrast,AlertDialogCancelwraps its button withAlertDialogPrimitive.Close, enabling auto-close behavior. Users will expect the action button to close the dialog by default (standard behavior in@base-ui/react), requiring them to manually addonClickhandlers or wrap the component themselves.Wrap
AlertDialogActionwithAlertDialogPrimitive.Closefor consistency:♻️ Suggested change
-function AlertDialogAction({ - className, - ...props -}: React.ComponentProps<typeof Button>) { - return ( - <Button - data-slot="alert-dialog-action" - className={cn(className)} - {...props} - /> - ) -} +function AlertDialogAction({ + className, + variant, + size, + ...props +}: AlertDialogPrimitive.Close.Props & + Pick<React.ComponentProps<typeof Button>, "variant" | "size">) { + return ( + <AlertDialogPrimitive.Close + data-slot="alert-dialog-action" + className={cn(className)} + render={<Button variant={variant} size={size} />} + {...props} + /> + ) +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/alert-dialog.tsx` around lines 144 - 155, AlertDialogAction currently returns a raw Button which does not auto-close the dialog; wrap the Button return value in AlertDialogPrimitive.Close (same pattern used by AlertDialogCancel) so clicking the action will dismiss the dialog by default, preserving className, data-slot and all passed props from the AlertDialogAction component.eslint.config.js-6-8 (1)
6-8:⚠️ Potential issue | 🟡 MinorEnsure directory patterns recursively ignore nested files.
ESLint's glob patterns require
/**suffix to match all nested files within a directory. Bare paths likesrc/components/uiwill not recursively ignore files insidesrc/components/ui/**.Suggested fix
- ignores: { - files: ['src/components/ui', 'src/components/animate-ui'], + ignores: { + files: ['src/components/ui/**', 'src/components/animate-ui/**'],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@eslint.config.js` around lines 6 - 8, The ignore patterns in the ignores.files array are not recursive; update the patterns used in the ignores object (the files array) to include a recursive glob (e.g., append /** or /**/* to each directory entry) so ESLint will ignore all nested files under those directories; modify the values currently 'src/components/ui' and 'src/components/animate-ui' in the ignores.files array accordingly.src/app/(locale)/[locale]/random-name/page.tsx-56-66 (1)
56-66:⚠️ Potential issue | 🟡 MinorResult keys can collide and trigger React duplicate-key warnings.
key: \${parts.adjective}-${parts.hero}`` is not guaranteed unique. With up to 20 generated names from a finite adjective/hero pool, duplicate combinations are realistic and will cause React to warn and reuse DOM nodes incorrectly (state may bleed across rows on regenerate). Either include the index in the key, or attach a stable id at generation time.♻️ Suggested fix
const resultItems = useMemo( () => - results.map((parts) => ({ - key: `${parts.adjective}-${parts.hero}`, + results.map((parts, index) => ({ + key: `${index}-${parts.adjective}-${parts.hero}`, text: formatRandomName(parts, { prefix, separator, }), })), [results, prefix, separator], )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(locale)/[locale]/random-name/page.tsx around lines 56 - 66, The computed list in resultItems (inside the useMemo that maps results) uses a non-unique key `${parts.adjective}-${parts.hero}` which can collide; change the key to be stable and unique by either including the map index or a stable id generated when names are created (e.g., attach id at generation time to each result object and use that id here), so update the mapping in the useMemo to use that unique identifier (reference: resultItems, useMemo, results, formatRandomName).src/components/animate-ui/components/base/tooltip.tsx-41-71 (1)
41-71:⚠️ Potential issue | 🟡 Minor
TooltipPanelPropsunions Positioner + Popup props but{...props}only reaches the Positioner.
TooltipPanelProps = TooltipPositionerPrimitiveProps & TooltipPopupPrimitiveProps, yet after destructuringclassName/style/sideOffset/children, the rest is forwarded only toTooltipPositionerPrimitive. Any caller passing popup-specific props (event handlers,id, motion props, etc.) will silently be applied to the positioner instead of the popup. Either restrict the type to the positioner and accept popup overrides through a dedicated prop, or split callers' props between the two primitives.♻️ Sketch of a split
-type TooltipPanelProps = TooltipPositionerPrimitiveProps & - TooltipPopupPrimitiveProps; +type TooltipPanelProps = TooltipPositionerPrimitiveProps & { + popupProps?: TooltipPopupPrimitiveProps; +}; @@ -function TooltipPanel({ - className, - sideOffset = 4, - children, - style, - ...props -}: TooltipPanelProps) { +function TooltipPanel({ + className, + sideOffset = 4, + children, + style, + popupProps, + ...positionerProps +}: TooltipPanelProps) { return ( <TooltipPortalPrimitive> <TooltipPositionerPrimitive sideOffset={sideOffset} className="z-50" - {...props} + {...positionerProps} > <TooltipPopupPrimitive className={cn( 'bg-primary text-primary-foreground w-fit origin-(--transform-origin) rounded-md px-3 py-1.5 text-xs text-balance', className, )} style={style} + {...popupProps} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/animate-ui/components/base/tooltip.tsx` around lines 41 - 71, TooltipPanel currently types props as TooltipPositionerPrimitiveProps & TooltipPopupPrimitiveProps but spreads the remaining props only onto TooltipPositionerPrimitive, causing popup-specific props to be misapplied; change TooltipPanel to destructure and separate positionerProps and popupProps (e.g., const { className, style, sideOffset = 4, children, ...rest } then split rest into positionerProps and popupProps based on known keys or by accepting explicit positionerProps and popupProps props) and forward positionerProps to TooltipPositionerPrimitive and popupProps to TooltipPopupPrimitive (ensuring className and style go to TooltipPopupPrimitive and sideOffset goes to TooltipPositionerPrimitive) so event handlers, id, motion props, etc. intended for the popup are applied to TooltipPopupPrimitive rather than the positioner.src/components/ui/chart.tsx-94-113 (1)
94-113:⚠️ Potential issue | 🟡 MinorTreat
config[*].color/config[*].theme[*]as trusted; consider basic validation.
ChartStyleinterpolatescolordirectly into a<style>block viadangerouslySetInnerHTML. Static analysis flagged it as XSS — in practice values are dev-supplied viaChartConfig, so this is low risk, but a stray string likered; } body { display:none } /*would break out of the declaration since there is no escaping. If chart configs are ever sourced from upstream/user-controlled data (e.g. CMS, query params), this becomes exploitable.Cheap mitigations: validate against a CSS color regex /
CSS.supports('color', value)before emitting, or move to inline style props per series and let React escape.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/chart.tsx` around lines 94 - 113, The inline style injection builds CSS using values from colorConfig and inserts them via dangerouslySetInnerHTML (see the style element that maps THEMES, id, and colorConfig) which can be broken out by untrusted strings; validate each color before interpolating (use CSS.supports('color', value) or a strict CSS color regex) and skip or replace invalid entries with a safe fallback (e.g., omit the --color- key or use a defaultColor) when constructing the map output. Update the code that maps over colorConfig (the anonymous mapping inside the style's __html) to call a small validator function (e.g., isValidCssColor) and only emit lines for colors that pass, or sanitize/encode them before concatenation. Ensure references in the fix point to THEMES, id, colorConfig and the style/dangerouslySetInnerHTML block so the change is localized and prevents XSS if configs become untrusted.src/components/animate-ui/primitives/base/switch.tsx-107-107 (1)
107-107:⚠️ Potential issue | 🟡 MinorTypo:
whileTap="tab"should be"tap".The root
SwitchuseswhileTap="tap"(line 70); the thumb uses"tab", which doesn't match any variant label. It's effectively a no-op today (novariantsmap is defined on the thumb), but the inconsistency is misleading and will silently skip the press animation if variants are later attached.✏️ Proposed fix
- whileTap="tab" + whileTap="tap"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/animate-ui/primitives/base/switch.tsx` at line 107, The thumb element inside the Switch component has a typo: it uses whileTap="tab" which doesn't match the variant name used by the root Switch; update the thumb's prop from "tab" to "tap" so it matches the root's whileTap="tap" and will trigger the intended press animation (look for the Switch component and the thumb element inside it to apply this change).src/components/ui/sidebar.tsx-292-292 (1)
292-292:⚠️ Potential issue | 🟡 MinorLikely RTL bug:
ltr:-translate-x-1/2 rtl:-translate-x-1/2apply the same shift in both directions.Both modifiers translate by
-50%. For a rail anchored on the opposite side under RTL, the offset typically needs to invert (rtl:translate-x-1/2). Worth double-checking against the visual in RTL mode — otherwise the rail will overlap the sidebar instead of sitting next to it.🔁 Probable fix
- "absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2", + "absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:translate-x-1/2",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/ui/sidebar.tsx` at line 292, The utility classes on the sidebar rail use identical negative X translations for both directions (the class string containing "ltr:-translate-x-1/2 rtl:-translate-x-1/2"), which causes the rail to overlap in RTL; update the RTL modifier to invert the shift (use "rtl:translate-x-1/2" instead of "rtl:-translate-x-1/2") in the sidebar component where that class string is defined so the rail is offset outward in RTL layouts.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ef6bf3a0-152e-4637-ba8d-3a2aef88e033
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (97)
.vscode/settings.jsoncomponents.jsoneslint.config.jsnext.config.tspackage.jsonsrc/app/(locale)/[locale]/base64-to-png/page.tsxsrc/app/(locale)/[locale]/base64/page.tsxsrc/app/(locale)/[locale]/battery/page.tsxsrc/app/(locale)/[locale]/code-diff/components/operator.tsxsrc/app/(locale)/[locale]/generate-query/page.tsxsrc/app/(locale)/[locale]/keep-screen-on/page.tsxsrc/app/(locale)/[locale]/miwifi-sn/page.tsxsrc/app/(locale)/[locale]/pinyin/page.tsxsrc/app/(locale)/[locale]/random-name/page.tsxsrc/app/(locale)/[locale]/vscode-extension-download/page.tsxsrc/app/_layout.tsxsrc/app/globals.csssrc/components/animate-ui/buttons/ripple.tsxsrc/components/animate-ui/components/base/switch.tsxsrc/components/animate-ui/components/base/tooltip.tsxsrc/components/animate-ui/components/buttons/button.tsxsrc/components/animate-ui/components/buttons/ripple.tsxsrc/components/animate-ui/primitives/animate/slot.tsxsrc/components/animate-ui/primitives/base/switch.tsxsrc/components/animate-ui/primitives/base/tooltip.tsxsrc/components/animate-ui/primitives/buttons/button.tsxsrc/components/animate-ui/primitives/buttons/ripple.tsxsrc/components/animate-ui/radix/switch.tsxsrc/components/animate-ui/radix/tooltip.tsxsrc/components/footer.tsxsrc/components/header.tsxsrc/components/show-case.tsxsrc/components/theme-switcher.tsxsrc/components/ui/accordion.tsxsrc/components/ui/alert-dialog.tsxsrc/components/ui/alert.tsxsrc/components/ui/aspect-ratio.tsxsrc/components/ui/avatar.tsxsrc/components/ui/badge.tsxsrc/components/ui/breadcrumb.tsxsrc/components/ui/button-group.tsxsrc/components/ui/button.tsxsrc/components/ui/calendar.tsxsrc/components/ui/card.tsxsrc/components/ui/carousel.tsxsrc/components/ui/chart.tsxsrc/components/ui/checkbox.tsxsrc/components/ui/collapsible.tsxsrc/components/ui/combobox.tsxsrc/components/ui/command.tsxsrc/components/ui/context-menu.tsxsrc/components/ui/dialog.tsxsrc/components/ui/direction.tsxsrc/components/ui/drawer.tsxsrc/components/ui/dropdown-menu.tsxsrc/components/ui/empty.tsxsrc/components/ui/field.tsxsrc/components/ui/hover-card.tsxsrc/components/ui/input-group.tsxsrc/components/ui/input-otp.tsxsrc/components/ui/input.tsxsrc/components/ui/item.tsxsrc/components/ui/kbd.tsxsrc/components/ui/label.tsxsrc/components/ui/menubar.tsxsrc/components/ui/native-select.tsxsrc/components/ui/navigation-menu.tsxsrc/components/ui/pagination.tsxsrc/components/ui/popover.tsxsrc/components/ui/progress.tsxsrc/components/ui/radio-group.tsxsrc/components/ui/resizable.tsxsrc/components/ui/scroll-area.tsxsrc/components/ui/select.tsxsrc/components/ui/separator.tsxsrc/components/ui/sheet.tsxsrc/components/ui/sidebar.tsxsrc/components/ui/skeleton.tsxsrc/components/ui/slider.tsxsrc/components/ui/sonner.tsxsrc/components/ui/spinner.tsxsrc/components/ui/switch.tsxsrc/components/ui/table.tsxsrc/components/ui/tabs.tsxsrc/components/ui/textarea.tsxsrc/components/ui/toggle-group.tsxsrc/components/ui/toggle.tsxsrc/components/ui/tooltip.tsxsrc/hooks/use-controlled-state.tsxsrc/hooks/use-mobile.tssrc/lib/get-strict-context.tsxsrc/lib/utils.tssrc/locales/en/messages.posrc/locales/zh/messages.posrc/providers/index.tsxsrc/utils/hero.tssrc/utils/random-name.ts
💤 Files with no reviewable changes (4)
- src/utils/hero.ts
- src/components/animate-ui/radix/switch.tsx
- src/components/animate-ui/buttons/ripple.tsx
- src/components/animate-ui/radix/tooltip.tsx
| "registries": { | ||
| "@animate-ui": "https://animate-ui.com/r/{name}.json" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Spot-check a couple of likely registry entries used by this PR.
for name in components/base/tooltip components/base/switch components/buttons/ripple primitives/base/tooltip; do
url="https://animate-ui.com/r/${name}.json"
echo "GET ${url}"
curl -sS -o /dev/null -w " status=%{http_code}\n" "${url}"
doneRepository: Debbl/tools.aiwan.run
Length of output: 350
@animate-ui registry URL template is broken — returns 404 for all test endpoints.
The registry entry uses https://animate-ui.com/r/{name}.json, but spot-checking several likely component paths (e.g., components/base/tooltip, components/base/switch, primitives/base/tooltip) all return HTTP 404. The registry endpoint must be fixed before merging so that shadcn add commands don't fail for users.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components.json` around lines 21 - 23, The `@animate-ui` registry entry is
using a broken URL template ("https://animate-ui.com/r/{name}.json") that
returns 404; update the "registries" mapping for the "@animate-ui" key to the
correct working template (or remove the entry) so shadcn add can resolve
components. Locate the "registries" object and change the value for
"@animate-ui" to the proper endpoint pattern (e.g., the actual host/path that
serves {name}.json), then verify by requesting a few known component names (like
components/base/tooltip) to ensure the template returns 200 before committing.
| import { Switch } from '~/components/animate-ui/components/base/switch' | ||
| import { | ||
| Tooltip, | ||
| TooltipContent, | ||
| TooltipProvider, | ||
| TooltipPanel, | ||
| TooltipTrigger, | ||
| } from '~/components/animate-ui/radix/tooltip' | ||
| } from '~/components/animate-ui/components/base/tooltip' | ||
| import { RippleButton } from '~/components/animate-ui/components/buttons/ripple' | ||
| import { TooltipProvider } from '~/components/animate-ui/primitives/base/tooltip' |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm whether components/base/tooltip re-exports TooltipProvider and that both
# files reference the same context source.
fd -p 'animate-ui/(components|primitives)/base/tooltip' -t f
fd -p 'animate-ui/(components|primitives)/base/tooltip' -t f -x rg -n -C2 'TooltipProvider|createContext|useContext' {}Repository: Debbl/tools.aiwan.run
Length of output: 1912
Import TooltipProvider from ~/components/animate-ui/components/base/tooltip for consistency.
Tooltip, TooltipTrigger, and TooltipPanel are imported from components/base/tooltip, but TooltipProvider is sourced from primitives/base/tooltip. The components module exports its own TooltipProvider wrapper; import all tooltip parts from the same components/base/tooltip module to ensure they share the correct context.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(locale)/[locale]/keep-screen-on/page.tsx around lines 8 - 15, The
TooltipProvider is being imported from primitives while Tooltip, TooltipTrigger,
and TooltipPanel come from components; change the import so TooltipProvider is
imported from the same module as the others (the components export) to ensure
they share the same context. Locate the imports for Tooltip, TooltipTrigger,
TooltipPanel and replace the TooltipProvider import to come from the same module
(the one that exports Tooltip, TooltipTrigger, TooltipPanel) so all tooltip
parts use the same provider.
| <SelectTrigger className='w-full'> | ||
| <SelectValue | ||
| placeholder={t`Separator`} | ||
| render={(props, state) => { | ||
| const option = separatorOptions.find( | ||
| (option) => option.value === state.value, | ||
| ) | ||
|
|
||
| return <span {...props}>{option?.label}</span> | ||
| }} | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Base UI react Select.Value render prop signature state.value v1.4
💡 Result:
In Base UI React v1.4 (released April 13, 2026), the Select.Value component renders a span element displaying the label of the currently selected item (when items prop is provided on Select.Root) or the raw value. The render prop (children function) signature is: (value: any) => React.ReactNode This formats the selected value. Example: <Select.Value> {(value) => value ? labels[value] : 'No value'} </Select.Value> SelectValueState interface (for render/className callbacks in some Base UI components): type SelectValueState = { value: any; placeholder: boolean; } No changes to Select.Value render prop in v1.4 release notes. Type definitions from @base-ui/react ~1.1.0 confirm the signature; recent PR #1562 (merged Mar 2025) passes value as second arg to children in some contexts, but primary render prop remains (value: any) => ReactNode. Official docs show single value param usage. Note: Base UI uses children as render prop (not separate render prop). For full state in render callbacks, use className={(state) => ...} where state includes value.
Citations:
- 1: https://base-ui.com/react/overview/releases/v1-4-0
- 2: https://cdn.jsdelivr.net/npm/@base-ui/react@1.1.0/select/value/SelectValue.d.ts
- 3: https://base-ui.com/react/components/select
- 4: https://base-ui.com/react/components/select/
- 5: [select] add "value" and other state to SelectValue.State mui/base-ui#2010
- 6: [select] Pass
valueas second argument to function childrenSelect.Valuemui/base-ui#1562
Fix incorrect SelectValue render prop usage.
Base UI's Select.Value does not have a render prop. It uses a children function with signature (value: any) => React.ReactNode (single value parameter, not props + state). Replace the current implementation with:
<SelectValue placeholder={t`Separator`}>
{(value) => {
const option = separatorOptions.find(
(option) => option.value === value,
)
return option?.label
}}
</SelectValue>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(locale)/[locale]/random-name/page.tsx around lines 130 - 140, The
SelectValue usage is incorrect: Base UI's Select.Value expects a children
function with signature (value) => ReactNode, not a render prop; update the
SelectValue instance (the component currently using render) to remove the render
prop and instead pass a children function that receives the selected value,
lookup the matching option from separatorOptions (same symbol) by option.value
=== value, and return option?.label; preserve the placeholder={t`Separator`}
prop.
| @@ -1,30 +1,10 @@ | |||
| @import 'tailwindcss'; | |||
| @import 'tw-animate-css'; | |||
| @import 'shadcn/tailwind.css'; | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the shadcn package exposes tailwind.css and inspect its package exports
fd -t d '^shadcn$' node_modules 2>/dev/null | head -5
fd 'tailwind.css' node_modules/shadcn 2>/dev/null
cat node_modules/shadcn/package.json 2>/dev/null | jq '{name, version, exports, files, main}'Repository: Debbl/tools.aiwan.run
Length of output: 47
🏁 Script executed:
# Check if node_modules exists and list shadcn-related packages
ls -la node_modules 2>/dev/null | grep -i shadcn || echo "No shadcn in node_modules"
find node_modules -maxdepth 2 -name "*shadcn*" -type d 2>/dev/null | head -20Repository: Debbl/tools.aiwan.run
Length of output: 91
🏁 Script executed:
# If shadcn exists, check its structure and package.json
if [ -d "node_modules/shadcn" ]; then
echo "=== shadcn package structure ==="
ls -la node_modules/shadcn/
echo "=== package.json ==="
cat node_modules/shadcn/package.json | head -50
fiRepository: Debbl/tools.aiwan.run
Length of output: 47
🏁 Script executed:
# Check if the import is used elsewhere in the codebase
rg "shadcn/tailwind" --type css --type js --type ts --type tsx 2>/dev/null || echo "No matches found"Repository: Debbl/tools.aiwan.run
Length of output: 82
🏁 Script executed:
# Look for any tailwind config to understand how tailwind is set up
fd -e "tailwind.config" -e "tailwind.config.ts" -e "tailwind.config.js" 2>/dev/null | head -5Repository: Debbl/tools.aiwan.run
Length of output: 47
🏁 Script executed:
# Check if globals.css exists and show its contents
cat -n src/app/globals.css 2>/dev/null || echo "File not found"Repository: Debbl/tools.aiwan.run
Length of output: 5655
🏁 Script executed:
# Check package.json to see declared dependencies
cat package.json | jq '.dependencies, .devDependencies' 2>/dev/null | head -50Repository: Debbl/tools.aiwan.run
Length of output: 1397
🏁 Script executed:
# Search for any tailwind or shadcn references in the project
rg "shadcn|@shadcn" --type json --type ts --type js --type css 2>/dev/null | head -20Repository: Debbl/tools.aiwan.run
Length of output: 219
🌐 Web query:
shadcn v4.5.0 package exports tailwind.css
💡 Result:
No, the shadcn (CLI) package at version 4.5.0 (or latest available ~4.4.0 as of Apr 2026) does not export a tailwind.css file. shadcn/ui is not a traditional npm component package but a CLI tool (npx shadcn@latest) for copying unstyled React components into your project. Tailwind CSS styles are configured in your own globals.css file via manual installation instructions, e.g., @import "tailwindcss"; @import "tw-animate-css"; @import "shadcn/tailwind.css"; but "shadcn/tailwind.css" refers to a registry or local file generated by the CLI, not an export from the shadcn npm package itself. The package structure includes index.js, index.d.ts, and tailwind.css (1.6KB), but no explicit "exports" map in package.json exposes tailwind.css for import like "./tailwind.css". Users must set up Tailwind separately and use the CLI to add components.
Citations:
- 1: https://registry.npmjs.org/shadcn
- 2: https://v4.shadcn.com/docs/installation/manual
- 3: https://ui.shadcn.com/docs/installation/manual
- 4: https://ui.shadcn.com/docs/tailwind-v4
- 5: https://github.com/shadcn-ui/ui/blob/main/packages/shadcn/CHANGELOG.md
- 6: https://github.com/anuoua/shadcn-packaged
🌐 Web query:
shadcn CLI tool tailwind css import path package structure
💡 Result:
The shadcn CLI (npx shadcn@latest init/add) configures Tailwind CSS integration via a components.json file created during init. It prompts for Tailwind paths: global CSS file (e.g., app/globals.css), tailwind.config.js location (e.g., tailwind.config.js; blank for Tailwind v4), prefix, baseColor, and cssVariables usage. These paths are stored in components.json under "tailwind": {"config": "...", "css": "...", ...}. The CLI uses them with tsconfig.json paths to place components and generate imports. Standard package structure (non-monorepo): - components/ui/ : UI primitives (e.g., components/ui/button.tsx) - lib/utils.ts : cn helper (clsx + tailwind-merge) - styles/globals.css : @import "tailwindcss/base"; @import "tailwindcss/components"; @import "tailwindcss/utilities"; plus CSS vars for theming Imports: import { Button } from "@/components/ui/button" (using @/ alias from tsconfig). In monorepos: Components in packages/ui/src/components/, app imports via "@workspace/ui/components/button". globals.css imports Tailwind directives and shadcn tailwind.css (manual install). CLI ensures content paths in tailwind.config.js include the components dir.
Citations:
- 1: https://ui.shadcn.com/docs/installation/manual
- 2: https://ui.shadcn.com/docs/components-json
- 3: https://ui.shadcn.com/docs/cli
- 4: https://ui.shadcn.com/docs/installation
- 5: https://ui.shadcn.com/docs/new
- 6: https://ui.shadcn.com/docs/monorepo
- 7: https://github.com/shadcn-ui/ui/blob/094edfcf/packages/shadcn/src/commands/init.ts
Remove or fix the shadcn/tailwind.css import—it is not a valid export from the shadcn package.
The shadcn CLI package (v4.5.0) includes a tailwind.css file internally but does not expose it via the package's exports field. While the file may exist at node_modules/shadcn/tailwind.css, importing it directly with @import 'shadcn/tailwind.css' is not a documented or supported approach. Standard Tailwind v4 setup uses:
`@import` 'tailwindcss';
`@import` 'tw-animate-css';
Consider removing this line or clarifying its purpose if custom shadcn styling is needed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/globals.css` at line 3, Remove the invalid import statement in
globals.css that reads "@import 'shadcn/tailwind.css';" because that file is not
exported by the shadcn package; either delete the line or replace it with the
standard Tailwind imports used by the project (use the Tailwind entry and any
animation plugin import you actually rely on), and if you need shadcn-specific
styles, generate or include them via the shadcn CLI workflow instead of
importing the unpublished package file.
| import * as React from "react" | ||
| import { mergeProps } from "@base-ui/react/merge-props" | ||
| import { useRender } from "@base-ui/react/use-render" | ||
| import { cva, type VariantProps } from "class-variance-authority" | ||
|
|
||
| import { cn } from "~/lib/utils" | ||
| import { Separator } from "~/components/ui/separator" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does @base-ui/react useRender require a "use client" boundary in Next.js App Router?
💡 Result:
Yes, using useRender from @base-ui/react requires a "use client" boundary in Next.js App Router.
Citations:
- 1: https://v5.mui.com/base-ui/guides/next-js-app-router/
- 2: https://base-ui.com/react/utils/use-render
- 3: [bug?][help] how do you use
renderprops in Next.js? mui/base-ui#4045 - 4: https://registry.npmjs.org/%40base-ui%2Freact
🏁 Script executed:
# Check if the file has "use client" directive at the top
head -20 src/components/ui/item.tsxRepository: Debbl/tools.aiwan.run
Length of output: 686
🏁 Script executed:
# Search for useRender usage in the file
rg "useRender" src/components/ui/item.tsx -B 2 -A 2Repository: Debbl/tools.aiwan.run
Length of output: 449
🏁 Script executed:
# Check the comparison files mentioned
head -5 src/components/ui/select.tsx
head -5 src/components/ui/radio-group.tsxRepository: Debbl/tools.aiwan.run
Length of output: 334
Add "use client" directive — useRender is a React hook.
Item calls useRender(...), which is a React hook that cannot be used in Server Components. In the Next.js App Router, this file will fail to render from a Server Component subtree without the "use client" boundary. Other hook-driven primitives in this PR (e.g., select.tsx, radio-group.tsx) declare "use client" for the same reason.
Proposed fix
+"use client"
+
import * as React from "react"
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"📝 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.
| import * as React from "react" | |
| import { mergeProps } from "@base-ui/react/merge-props" | |
| import { useRender } from "@base-ui/react/use-render" | |
| import { cva, type VariantProps } from "class-variance-authority" | |
| import { cn } from "~/lib/utils" | |
| import { Separator } from "~/components/ui/separator" | |
| "use client" | |
| import * as React from "react" | |
| import { mergeProps } from "@base-ui/react/merge-props" | |
| import { useRender } from "@base-ui/react/use-render" | |
| import { cva, type VariantProps } from "class-variance-authority" | |
| import { cn } from "~/lib/utils" | |
| import { Separator } from "~/components/ui/separator" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/ui/item.tsx` around lines 1 - 7, This file uses the React hook
useRender inside the Item component, so add the "use client" directive as the
very first line of src/components/ui/item.tsx to mark it a client component;
ensure the string literal "use client" appears before any imports so hooks
(useRender) can be used safely (follow the same pattern used in select.tsx and
radio-group.tsx).
| <NavigationMenuPrimitive.Content | ||
| data-slot="navigation-menu-content" | ||
| className={cn( | ||
| "data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto p-2 pr-2.5 transition-[opacity,transform,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:opacity-0 data-starting-style:opacity-0 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Tailwind CSS v4 syntax for chaining a boolean data variant with a value-based data variant (e.g. data-ending-style with data-[activation-direction=left])
💡 Result:
In Tailwind CSS v4, you chain a boolean data variant (existence of attribute, e.g. data-active) with a value-based data variant (specific value, e.g. data-[activation-direction=left]) by simply juxtaposing the variant prefixes before the utility class. Syntax: data-[activation-direction=left]:data-active:bg-green-500 This generates CSS targeting elements where both data-activation-direction="left" AND data-active is present. For the example with data-ending-style (assuming boolean like data-ending-style present, and data-[activation-direction=left]): data-[activation-direction=left]:data-ending-style:bg-blue-500 This works due to v4's composable variants support, where data-* variants (both boolean like data-active: and value-based like data-[foo=bar]:) can be chained with other variants including each other, group-, peer-, etc. Examples from docs and beta: - data-selected:opacity-100 (boolean data-selected) - data-[size=large]:p-8 (value-based) - Chaining examples like group-data-[size=large]:p-8, not-data-active:underline, etc. No configuration needed; dynamic data-* variants work out-of-the-box in v4.
Citations:
- 1: https://tailwindcss.com/blog/tailwindcss-v4
- 2: https://v3.tailwindcss.com/docs/v4-beta
- 3: https://tailwindcss.com/docs/pseudo-class-variants
- 4: https://www.tailwindcss.com
- 5: Add dynamic
data-*variant tailwindlabs/tailwindcss#9559
🏁 Script executed:
fd navigation-menu.tsx --type fRepository: Debbl/tools.aiwan.run
Length of output: 103
🏁 Script executed:
cat -n src/components/ui/navigation-menu.tsx | sed -n '80,95p'Repository: Debbl/tools.aiwan.run
Length of output: 2114
🏁 Script executed:
# Search for context on how data-ending-style and data-activation-direction are used
rg "data-ending-style|data-activation-direction" -A 2 -B 2 --type tsx --type tsRepository: Debbl/tools.aiwan.run
Length of output: 93
🏁 Script executed:
rg "data-ending-style|data-activation-direction" -A 2 -B 2Repository: Debbl/tools.aiwan.run
Length of output: 5127
🏁 Script executed:
# Verify the chaining pattern in sheet.tsx more systematically
cat -n src/components/ui/sheet.tsx | sed -n '35,48p'Repository: Debbl/tools.aiwan.run
Length of output: 393
🏁 Script executed:
# Get the exact line with the sheet.tsx pattern to confirm the chaining order
cat -n src/components/ui/sheet.tsx | grep -A 1 "data-\[side" | head -20Repository: Debbl/tools.aiwan.run
Length of output: 1290
🏁 Script executed:
# Double-check the navigation-menu.tsx Popup element pattern for consistency
cat -n src/components/ui/navigation-menu.tsx | sed -n '100,110p'Repository: Debbl/tools.aiwan.run
Length of output: 440
🏁 Script executed:
# Find the Popup className in navigation-menu.tsx
cat -n src/components/ui/navigation-menu.tsx | sed -n '115,130p'Repository: Debbl/tools.aiwan.run
Length of output: 1118
Incorrect Tailwind variant chaining order in the suggested fix.
The bracketing issue is correctly identified—value-based data attributes must use brackets (data-[activation-direction=left]: not data-activation-direction=left:). However, the suggested fix has the chaining order backwards. In Tailwind v4, value-based variants must come before boolean variants. The pattern used elsewhere in the codebase (e.g., sheet.tsx) confirms this: data-[side=bottom]:data-ending-style:... not data-ending-style:data-[side=bottom]:....
The four translate classes should be:
- "data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] ...",
+ "data-[activation-direction=left]:data-ending-style:translate-x-[50%] data-[activation-direction=right]:data-ending-style:translate-x-[-50%] data-[activation-direction=left]:data-starting-style:translate-x-[-50%] data-[activation-direction=right]:data-starting-style:translate-x-[50%] ...",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/ui/navigation-menu.tsx` at line 87, The Tailwind variant
chaining order in the navigation-menu class string is wrong: value-based data
variants must come before boolean variants, so replace patterns like
data-ending-style:data-activation-direction=left:... with
data-[activation-direction=left]:data-ending-style:... (and likewise for the
other three translate cases) in the class on the NavigationMenu/slot usage in
src/components/ui/navigation-menu.tsx; ensure you follow the established pattern
used in sheet.tsx (e.g., data-[side=bottom]:data-ending-style:...) so the four
translate classes use
data-[activation-direction=left/right]:data-ending-style:... and
data-[activation-direction=left/right]:data-starting-style:... ordering.
| function NavigationMenuIndicator({ | ||
| className, | ||
| ...props | ||
| }: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) { | ||
| return ( | ||
| <NavigationMenuPrimitive.Icon | ||
| data-slot="navigation-menu-indicator" | ||
| className={cn( | ||
| "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in", | ||
| className | ||
| )} | ||
| {...props} | ||
| > | ||
| <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" /> | ||
| </NavigationMenuPrimitive.Icon> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does @base-ui-components/react navigation-menu expose an Indicator primitive equivalent to Radix's NavigationMenu.Indicator, and what data-state attributes does NavigationMenu.Icon emit?
💡 Result:
No, @base-ui-components/react navigation-menu does not expose an Indicator primitive equivalent to Radix's NavigationMenu.Indicator. The official documentation lists components like Root, List, Item, Trigger, Icon, Content, Portal, Backdrop, Positioner, Popup, Arrow, and Viewport, but no Indicator is mentioned for active item highlighting. NavigationMenu.Icon emits data attributes based on its open state. The source code shows it uses triggerOpenStateMapping, which applies standard open/closed attributes like data-state="open" or data-state="closed" when the associated menu item is active and open. It renders as a span with aria-hidden="true" containing a ▼ icon, updating its state when the popup is open for that item.
Citations:
- 1: https://base-ui.com/react/components/navigation-menu
- 2: https://github.com/mui/base-ui/blob/d81ec002/packages/react/src/navigation-menu/icon/NavigationMenuIcon.tsx
- 3: https://www.flowbite.com
- 4: https://github.com/mui/base-ui/blob/d81ec002/packages/react/src/navigation-menu/root/NavigationMenuRoot.tsx
- 5: https://github.com/mui/base-ui/blob/d81ec002/packages/react/src/navigation-menu/item/NavigationMenuItem.tsx
NavigationMenuIndicator is wired to the wrong primitive.
NavigationMenuPrimitive.Icon in Base UI is the dropdown chevron rendered inside Trigger ("an icon that indicates that the trigger button opens a menu") — it is not the Radix-style active-trigger indicator. Consequences:
- It expects to be a child of
Trigger, not a sibling positioned attop-full. Rendering it outside aTriggerwill not produce the active-tracker arrow this file implies. - The
data-[state=hidden]/data-[state=visible]animation selectors on line 148 will not match: Base UI'sIconemitsdata-state="open"ordata-state="closed"based on the menu's open state, not thehidden/visiblevariants you're targeting. The in/out animations will never trigger. - This export name (
NavigationMenuIndicator) will mislead consumers who expect Radix semantics.
Base UI's navigation menu does not ship an active-trigger Indicator primitive—the active-content reveal is handled via Viewport/Popup transitions on Positioner. Either remove NavigationMenuIndicator entirely or build it from Positioner/custom logic; do not alias Icon to it.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/ui/navigation-menu.tsx` around lines 140 - 156, The
NavigationMenuIndicator component is incorrectly using
NavigationMenuPrimitive.Icon (meant as a Trigger child) and wrong data-state
selectors; remove or replace it: either delete the NavigationMenuIndicator
export and any imports/uses referencing it, or re-implement it using the menu
Positioner/Viewport/Popup (or custom logic) to render the active-trigger arrow
and use the correct state attributes (Positioner/Viewport-driven transitions)
instead of NavigationMenuPrimitive.Icon and the data-[state=hidden|visible]
selectors; update references to the component accordingly and ensure the Trigger
children continue to use NavigationMenuPrimitive.Icon for chevrons.
| function Tabs({ | ||
| className, | ||
| orientation = "horizontal", | ||
| ...props | ||
| }: TabsPrimitive.Root.Props) { | ||
| return ( | ||
| <TabsPrimitive.Root | ||
| data-slot="tabs" | ||
| data-orientation={orientation} | ||
| className={cn( | ||
| "group/tabs flex gap-2 data-horizontal:flex-col", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does @base-ui/reactTabs.Root require anorientation prop for keyboard navigation and ARIA, or does it auto-detect from data-orientation?
💡 Result:
No, @base-ui/react Tabs.Root does not require an orientation prop for keyboard navigation and ARIA attributes. The prop is optional with a default value of 'horizontal'. When omitted, it uses horizontal orientation, which sets data-orientation="horizontal" on relevant elements and handles keyboard navigation accordingly (e.g., left/right arrows). For vertical orientation, explicitly provide orientation="vertical". There is no auto-detection from data-orientation or CSS layout; orientation is controlled solely by the prop.
Citations:
- 1: https://base-ui.com/react/components/tabs
- 2: https://cdn.jsdelivr.net/npm/@base-ui/react@1.3.0/esm/tabs/root/TabsRoot.d.ts
- 3: https://github.com/mui/base-ui/blob/d81ec002/packages/react/src/tabs/root/TabsRoot.tsx
🏁 Script executed:
cat -n src/components/ui/tabs.tsx | head -30Repository: Debbl/tools.aiwan.run
Length of output: 1087
orientation is destructured but never forwarded to TabsPrimitive.Root.
orientation is pulled out of the props bag on line 10 and only echoed back as a DOM data-orientation attribute on line 16. It is not passed to the underlying <TabsPrimitive.Root>, so the primitive always uses its default orientation. Two consequences:
- Keyboard navigation stays horizontal even for
orientation="vertical"(Left/Right vs Up/Down). - ARIA
aria-orientationand the primitive's owndata-orientationwon't match the visual layout, and conflict with the manually setdata-orientation(last-write-wins on the same DOM attribute).
The hand-rolled data-orientation on the wrapper becomes redundant once the primitive receives the orientation prop and sets it correctly.
🛠 Proposed fix
function Tabs({
className,
orientation = "horizontal",
...props
}: TabsPrimitive.Root.Props) {
return (
<TabsPrimitive.Root
data-slot="tabs"
+ orientation={orientation}
- data-orientation={orientation}
className={cn(
"group/tabs flex gap-2 data-horizontal:flex-col",
className
)}
{...props}
/>
)
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/ui/tabs.tsx` around lines 8 - 24, The Tabs component
destructures orientation but fails to pass it into TabsPrimitive.Root, so
forward the orientation prop into TabsPrimitive.Root (e.g., add
orientation={orientation}) and remove the manual data-orientation attribute from
the wrapper; ensure TabsPrimitive.Root receives the original orientation value
so keyboard/ARIA orientation and the primitive's internal state are correct
(update references inside the Tabs function around TabsPrimitive.Root and the
props spread).
| #: src/app/(locale)/[locale]/random-name/page.tsx:102 | ||
| #: src/app/(locale)/[locale]/random-name/page.tsx:103 | ||
| msgid "Underscore" | ||
| msgstr "VSIX URL" |
There was a problem hiding this comment.
Critical: Underscore is mistranslated to VSIX URL.
The English msgstr for the “Underscore” key is "VSIX URL", which will surface in the UI wherever the random-name page renders the “Underscore” option label. Almost certainly a paste error from the nearby VSIX URL entry.
💚 Proposed fix
#: src/app/(locale)/[locale]/random-name/page.tsx:102
#: src/app/(locale)/[locale]/random-name/page.tsx:103
msgid "Underscore"
-msgstr "VSIX URL"
+msgstr "Underscore"Worth re-running lingui extract/compile to make sure no other msgstr got similarly stale.
📝 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.
| #: src/app/(locale)/[locale]/random-name/page.tsx:102 | |
| #: src/app/(locale)/[locale]/random-name/page.tsx:103 | |
| msgid "Underscore" | |
| msgstr "VSIX URL" | |
| #: src/app/(locale)/[locale]/random-name/page.tsx:102 | |
| #: src/app/(locale)/[locale]/random-name/page.tsx:103 | |
| msgid "Underscore" | |
| msgstr "Underscore" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/locales/en/messages.po` around lines 322 - 325, The PO entry for msgid
"Underscore" is incorrectly translated as "VSIX URL"; update the msgstr for that
entry to the correct English label "Underscore" (the msgid "Underscore" in
src/locales/en/messages.po), then re-run your localization pipeline (e.g.,
lingui extract/compile) so compiled catalogs reflect the fix and ensure no other
msgstrs were accidentally pasted from the VSIX URL entry referenced by
src/app/(locale)/[locale]/random-name/page.tsx.
✅ Deploy Preview for tools-aiwan-run ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Summary by CodeRabbit
New Features
Bug Fixes & Improvements
Styling & Design