Feature/UI revamp shadcn#7
Conversation
- setup shadcn/ui - Introduced a new button component with variants and sizes using class-variance-authority. - Added a components configuration file for Shadcn UI. - Updated index.css to include new styles and themes. - Added utility functions for class name management. - Updated package.json and package-lock.json to include new dependencies. - Configured TypeScript paths for easier imports. - Updated Vite configuration to resolve aliases for cleaner imports.
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR implements a complete frontend UI revamp using shadcn/ui, adding a comprehensive component library and refactoring all pages (Login, Register, Dashboard, Resources, Solver) with new responsive layouts, plus Docker deployment infrastructure. ChangesFrontend UI Revamp with Shadcn/UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 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)
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 |
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/Resources.tsx (1)
82-85:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReset the native file input after a successful upload.
setFile(null)only clears React state. The browser still keeps the previous file input value, so picking the same file again will not fireonChange.Suggested fix
<input id="file" type="file" accept=".pdf,.txt" + onClick={(e) => { + e.currentTarget.value = ''; + }} onChange={(e) => setFile(e.target.files?.[0] || null)} className="absolute inset-0 opacity-0 cursor-pointer" />Also applies to: 222-227
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/Resources.tsx` around lines 82 - 85, The onSuccess handler currently calls setFile(null) which only clears React state but leaves the native file input value intact; add a ref to the file input (e.g., fileInputRef) and in the onSuccess handlers (the one shown and the other at lines 222-227) set fileInputRef.current.value = '' (or otherwise reset the input element) in addition to setFile(null), so the browser will allow selecting the same file again.
♻️ Duplicate comments (2)
frontend/src/components/ui/scroll-area.tsx (1)
4-4:⚠️ Potential issue | 🔴 CriticalVerify the
radix-uipackage import path.Same issue as the other files: non-standard import from
"radix-ui"instead of@radix-ui/react-scroll-area.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/ui/scroll-area.tsx` at line 4, The import path for ScrollArea is incorrect—replace the non-standard import from "radix-ui" with the official package import so the ScrollArea primitive resolves correctly; update the import that brings in ScrollArea as ScrollAreaPrimitive (used in this file) to import from "`@radix-ui/react-scroll-area`" so subsequent usages of ScrollAreaPrimitive work without module resolution errors.frontend/src/components/ui/select.tsx (1)
2-2:⚠️ Potential issue | 🔴 CriticalVerify the
radix-uipackage import path.Same issue as dropdown-menu.tsx: the import uses
from "radix-ui"instead of the standard scoped package format. This should typically be@radix-ui/react-selectbased on official Radix UI documentation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/ui/select.tsx` at line 2, The import in select.tsx uses an incorrect package path "radix-ui"; update the import to the proper Radix package (e.g., change `import { Select as SelectPrimitive } from "radix-ui"` to import from `@radix-ui/react-select`) so the `Select` (aliased as `SelectPrimitive`) resolves correctly; ensure the named import remains `Select as SelectPrimitive` and adjust any other import variants in this file to the same scoped package.
🧹 Nitpick comments (7)
frontend/src/App.tsx (2)
16-16: ⚡ Quick winRemove unused import.
SidebarProvideris imported but not used anywhere in the visible code. OnlyTooltipProvideris actually used to wrap the application routes.🧹 Proposed cleanup
-import { SidebarProvider } from "`@/components/ui/sidebar`" import { TooltipProvider } from "`@/components/ui/tooltip`"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/App.tsx` at line 16, Remove the unused import SidebarProvider from the top of the file; keep TooltipProvider which is actually used to wrap the application routes. Locate the import statement that includes SidebarProvider (the one that also imports from "`@/components/ui/sidebar`") and delete SidebarProvider from that import so only TooltipProvider (and any other used symbols) remain.
109-115: ⚡ Quick winConsider making the quota value configurable.
The quota value
30is hardcoded in both the progress calculation (line 112) and the display text (line 115). If different user plans have different quotas, this value should come from the user object or a plan configuration.💡 Suggested approach
If the backend provides a quota field:
<CardTitle className="text-3xl font-black text-green-600 dark:text-green-500"> {displayUser?.questions_used} <span className="text-sm font-medium text-muted-foreground uppercase tracking-normal">Questions Answered</span> </CardTitle>+const quota = displayUser?.quota || 30; <div className="flex items-center gap-4"> <div className="flex-1 h-2 bg-muted rounded-full overflow-hidden"> <div className="h-full bg-green-500 transition-all duration-1000 ease-out" - style={{ width: `${Math.min((displayUser?.questions_used || 0) / 30 * 100, 100)}%` }} + style={{ width: `${Math.min((displayUser?.questions_used || 0) / quota * 100, 100)}%` }} /> </div> - <span className="text-[10px] font-bold text-muted-foreground uppercase whitespace-nowrap">Quota: {displayUser?.questions_used} / 30</span> + <span className="text-[10px] font-bold text-muted-foreground uppercase whitespace-nowrap">Quota: {displayUser?.questions_used} / {quota}</span> </div>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/App.tsx` around lines 109 - 115, The hardcoded quota value 30 is used in the progress width and display; update the component to read a quota property (e.g., displayUser?.quota or displayUser?.questions_quota) and use that for both the Math.min((displayUser?.questions_used || 0) / quota * 100, 100) calculation and the displayed string "Quota: X / Y", with a sensible fallback (e.g., 30) if the quota field is missing; modify references around displayUser and questions_used in App.tsx so both progress bar style and the span use the same quota variable.frontend/src/pages/Register.tsx (1)
85-92: ⚡ Quick winConsider adding ARIA attributes for better accessibility.
Similar to the Login page, adding
aria-invalidandaria-describedbyattributes will improve screen reader support when validation errors occur.♿ Proposed accessibility enhancement
<Input id="email" type="email" placeholder="name@example.com" {...register('email')} + aria-invalid={!!errors.email} + aria-describedby={errors.email ? "email-error" : undefined} className={errors.email ? "border-destructive focus-visible:ring-destructive" : ""} /> -{errors.email && <p className="text-destructive text-xs italic">{errors.email.message}</p>} +{errors.email && <p id="email-error" className="text-destructive text-xs italic" role="alert">{errors.email.message}</p>}Apply similar changes to password (lines 96-102) and confirmPassword (lines 106-112) fields.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/Register.tsx` around lines 85 - 92, Add ARIA attributes to form inputs to improve screen-reader validation feedback: for the email Input (where register('email') is used and errors.email is checked) set aria-invalid={!!errors.email} and aria-describedby to the id of the error paragraph (e.g., "email-error") when an error exists, and give the error <p> that id; repeat the same pattern for the password and confirmPassword Inputs (where register('password') and register('confirmPassword') are used and errors.password / errors.confirmPassword are checked) so each input has aria-invalid and aria-describedby pointing to its corresponding error element.frontend/src/pages/Login.tsx (1)
87-94: ⚡ Quick winConsider adding ARIA attributes for better accessibility.
When validation errors occur, screen readers should be notified. Add
aria-invalidandaria-describedbyto improve the experience for users with assistive technologies.♿ Proposed accessibility enhancement
<Input id="email" type="email" placeholder="name@example.com" {...register('email')} + aria-invalid={!!errors.email} + aria-describedby={errors.email ? "email-error" : undefined} className={errors.email ? "border-destructive focus-visible:ring-destructive" : ""} /> -{errors.email && <p className="text-destructive text-xs italic">{errors.email.message}</p>} +{errors.email && <p id="email-error" className="text-destructive text-xs italic" role="alert">{errors.email.message}</p>}Apply similar changes to the password field (lines 98-104).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/Login.tsx` around lines 87 - 94, Add ARIA attributes to the email (and similarly the password) input to expose validation state to assistive tech: set aria-invalid to Boolean(errors.email) and set aria-describedby to the id of the error message (e.g., `${id}-error`), and ensure the error <p> has that same id (e.g., "email-error") so screen readers announce it; update the JSX for the Input using register('email') and the error <p> (errors.email) accordingly and mirror the same changes for the password input and its error paragraph.frontend/src/components/AppSidebar.tsx (2)
73-92: 💤 Low valueExact path matching may not highlight active state for nested routes.
Line 77 uses
location.pathname === item.pathfor exact matching. If routes like/resources/:idexist, the "Resources" nav item won't be highlighted when viewing a specific resource.Consider using
startsWithfor parent routes:♻️ Suggested fix for nested route matching
<SidebarMenuButton asChild - isActive={location.pathname === item.path} + isActive={item.path === "/" + ? location.pathname === "/" + : location.pathname.startsWith(item.path)} tooltip={item.label}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/AppSidebar.tsx` around lines 73 - 92, The active-state check uses exact equality (location.pathname === item.path) so parent nav items in navItems (e.g., "Resources") won't be highlighted for nested routes like /resources/123; update the SidebarMenuButton isActive logic to consider prefix matching (e.g., location.pathname.startsWith(item.path)) or a more robust helper that treats "/" specially and avoids false positives for similarly prefixed paths; change the comparison where SidebarMenuButton is rendered (the location.pathname === item.path expression) to this startsWith-based check and adjust the className conditional to use the same helper.
39-49: ⚡ Quick winDark mode preference is not persisted.
The current implementation toggles dark mode via
document.documentElement.classListbut doesn't persist the preference tolocalStorage. Users will lose their theme selection on page refresh.Consider persisting to localStorage and initializing from it:
♻️ Suggested persistence approach
const [isDark, setIsDark] = useState( - document.documentElement.classList.contains('dark') + () => { + const stored = localStorage.getItem('theme'); + if (stored) return stored === 'dark'; + return document.documentElement.classList.contains('dark'); + } ); useEffect(() => { if (isDark) { document.documentElement.classList.add('dark'); + localStorage.setItem('theme', 'dark'); } else { document.documentElement.classList.remove('dark'); + localStorage.setItem('theme', 'light'); } }, [isDark]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/AppSidebar.tsx` around lines 39 - 49, Initialize and persist the dark-mode preference: change the isDark state initialization to read from localStorage (fallback to document.documentElement.classList.contains('dark') or a system preference) and update localStorage whenever isDark changes; specifically update the useState initializer for isDark and add/localize the side-effect in the useEffect that runs on [isDark] to both add/remove the 'dark' class on document.documentElement and call localStorage.setItem('theme' or similar, isDark ? 'dark' : 'light'), keeping the existing class-add/remove logic in the effect and keeping setIsDark usage unchanged.frontend/src/components/ui/alert-dialog.tsx (1)
58-60: 💤 Low valueRedundant size variants set identical max-width.
Both
data-[size=default]:max-w-xsanddata-[size=sm]:max-w-xsapply the same constraint. Consider simplifying tomax-w-xsunless different sizes are planned.♻️ Simplification
className={cn( - "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", + "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/ui/alert-dialog.tsx` around lines 58 - 60, The class list in alert-dialog.tsx contains redundant size variants (data-[size=default]:max-w-xs and data-[size=sm]:max-w-xs) that both set the same max-width; update the className passed to cn inside the alert-dialog component to remove the duplicate data-[size=sm]:max-w-xs (or replace both with a single max-w-xs) so the element uses a single, explicit max-width rule; locate the string passed to cn in the alert dialog's JSX (the group/alert-dialog-content class block) and simplify the size-related classes accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/Dockerfile`:
- Around line 12-18: The container runs Nginx as root; modify the Dockerfile to
create or use a non-root user and switch to it (e.g., create a user/group or use
the existing nginx user) after copying assets and set proper ownership of
/usr/share/nginx/html so Nginx can serve files without root; also align the
exposed port with the updated nginx.conf (change EXPOSE from 80 to 8080) and
keep CMD ["nginx", "-g", "daemon off;"] (nginx.conf will bind to 8080), ensuring
USER is set after the files are owned by the non-root user and before the final
CMD.
In `@frontend/package.json`:
- Line 27: The package.json currently lists "shadcn" as a runtime dependency;
move it into devDependencies instead. Edit package.json to remove the "shadcn":
"^4.7.0" entry from the top-level "dependencies" object and add the same
"shadcn": "^4.7.0" entry under "devDependencies" so it remains available for
CLI/component generation but is not installed in production.
In `@frontend/src/components/AppSidebar.tsx`:
- Around line 109-111: In AppSidebar, the line rendering user?.plan can output
"undefined/null Plan"; update the rendering in the AppSidebar component to use a
safe fallback (e.g., a default plan label like "Free" or "Unknown") when
user?.plan is null/undefined and ensure capitalization remains; locate the span
that currently shows {user?.plan} Plan and replace it with a fallback expression
or conditional that supplies the default value so the UI never shows "undefined"
or "null".
In `@frontend/src/components/ui/dialog.tsx`:
- Around line 70-82: The close icon currently uses the default 24×24px size
while the surrounding Button uses size="icon-sm" (16×16px); update the XIcon
inside the DialogPrimitive.Close/Button to explicitly match the button by
passing a size or a className (e.g., size={16} or className="w-4 h-4") so the
icon scales to the icon-sm button—locate the XIcon in the showCloseButton block
within DialogPrimitive.Close and adjust its props accordingly.
In `@frontend/src/components/ui/dropdown-menu.tsx`:
- Line 4: The import for the Radix dropdown is incorrect and will fail at
runtime; update the import that currently brings in DropdownMenuPrimitive from
"radix-ui" to import { DropdownMenu as DropdownMenuPrimitive } from
"`@radix-ui/react-dropdown-menu`" and ensure the package
`@radix-ui/react-dropdown-menu` is added to package.json dependencies so the
DropdownMenuPrimitive symbol resolves at runtime.
In `@frontend/src/components/ui/scroll-area.tsx`:
- Line 21: The Tailwind class in the ScrollArea component's className uses
"outline-none" which in Tailwind v4 only removes the outline style; update the
className in frontend/src/components/ui/scroll-area.tsx (the JSX element
containing className="... rounded-[inherit] transition-[color,box-shadow]
outline-none ...") to use "outline-hidden" instead of "outline-none" so the
outline is fully removed per Tailwind v4 breaking changes.
In `@frontend/src/components/ui/select.tsx`:
- Line 45: The Tailwind v4 breaking changes require updating the class string in
the Select component: replace the `outline-none` token with `outline-hidden` and
change the arbitrary rounded token `rounded-[min(var(--radius-md),10px)]` to the
parentheses form `rounded-(min(var(--radius-md),10px))`; locate the className in
frontend/src/components/ui/select.tsx (the long class string used for the Select
wrapper) and make those two substitutions so the component complies with
Tailwind v4.
In `@frontend/src/components/ui/separator.tsx`:
- Around line 17-19: The separator's className is using non-matching selectors
`data-horizontal`/`data-vertical`, so update the selector strings in the
className for the Separator component (the cn(...) call in
frontend/src/components/ui/separator.tsx) to use Radix's attribute selectors
[data-orientation="horizontal"] and [data-orientation="vertical"] (e.g. replace
`data-horizontal:h-px data-horizontal:w-full data-vertical:w-px
data-vertical:self-stretch` with `[data-orientation="horizontal"]:h-px
[data-orientation="horizontal"]:w-full [data-orientation="vertical"]:w-px
[data-orientation="vertical"]:self-stretch`) so the horizontal/vertical sizing
applies correctly.
In `@frontend/src/components/ui/sheet.tsx`:
- Around line 71-83: The close icon in the sheet uses XIcon without an explicit
size, causing a visual mismatch with the Button sized "icon-sm"; update the JSX
inside the SheetPrimitive.Close (the Button containing XIcon) to pass an
explicit size prop to XIcon (matching dialog.tsx usage) so the icon dimensions
align with the Button; locate the XIcon instance inside the showCloseButton
block and add the appropriate size value (e.g., size={16}) to the XIcon element.
In `@frontend/src/components/ui/sidebar.tsx`:
- Line 3: The import of Slot is wrong — replace any occurrences of `import {
Slot } from "radix-ui"` with `import { Slot } from "`@radix-ui/react-slot`"`
(e.g., in components that reference Slot such as the Sidebar component in
sidebar.tsx and similar imports in button.tsx and badge.tsx), and add
`@radix-ui/react-slot` to package.json dependencies and run your package manager
to install it so the new import resolves.
In `@frontend/src/components/ui/tabs.tsx`:
- Around line 27-40: tabsListVariants is using incorrect data selectors
(group-data-horizontal/tabs and group-data-vertical/tabs) that don't match the
actual data-orientation attribute; update the variant class string inside the
cva call to use selectors that match data-orientation, e.g. replace
group-data-horizontal/tabs with group-[data-orientation=horizontal]/tabs and
group-data-vertical/tabs with group-[data-orientation=vertical]/tabs so the
horizontal/vertical styles apply correctly.
- Around line 15-24: Update all uses of custom data-attribute variants to
Tailwind's bracket notation: anywhere you set data-orientation={orientation}
(e.g., in TabsPrimitive.Root and related components) replace occurrences of the
undefined variant syntax like "data-horizontal:flex-col" with the bracket form
"data-[orientation=horizontal]:flex-col"; do the same pattern-mapping for other
data-attributes found in separator.tsx and scroll-area.tsx so every class
conditional targeting data-attributes uses data-[attr=value]:... instead of
data-<name>:... (search for strings like data-horizontal, data-vertical, etc.,
and change them to data-[orientation=horizontal]:...,
data-[orientation=vertical]:..., or the appropriate attribute/value pair).
In `@frontend/src/pages/Resources.tsx`:
- Around line 332-345: Buttons that are icon-only (e.g., the Button wrapping
<Check />, <Edit2 /> and other icon actions) lack accessible names; update the
icon-only controls used for rename (startRenaming, handleRename), open, retry,
stop, and delete to include an explicit accessible label by adding a descriptive
aria-label (or title) and/or a visually-hidden text node for screen readers,
matching the same pattern used for the rename button; apply this change for all
occurrences including the block around startRenaming/handleRename and the other
actions referenced in the 373-409 range so each Button clearly announces its
purpose to assistive tech.
- Around line 379-388: The current UI uses a single retryMutation.isPending for
all rows, causing every failed row to disable/animate when one retry runs;
change to track pending retries per resource (by id) instead: create a local
state map/set (e.g., pendingRetryIds) and update it in the retry flow (add id on
mutate/start and remove on success/error/settle) or use per-id mutations so each
call has its own isPending; then replace references to retryMutation.isPending
with pendingRetryIds.has(res.id) (and use that same check to add the
"animate-spin" class to <RefreshCw />) and ensure you update this set inside
retryMutation's onMutate/onSettled (or create a per-row useMutation that calls
the same retry API with res.id) so only the clicked row shows disabled/spinning.
In `@frontend/src/pages/Solver.tsx`:
- Around line 216-223: The three "New Session" Buttons (the one shown and the
collapsed-sidebar and desktop-sidebar variants referenced around lines 385-390
and 397-404) are not guarded against duplicate clicks; update each Button that
calls createSessionMutation.mutate() to be disabled while the mutation is in
flight by using createSessionMutation.isLoading (or isLoading/isMutating from
your mutation hook) as the disabled prop (and optionally adjust
className/aria-busy). Ensure you apply the same change to the Button instances
that reference createSessionMutation to prevent multiple rapid session
creations.
- Around line 232-240: The UI treats failed fetches the same as "no data"
because the render logic only checks sessionsLoading and sessions?.length; fix
this by surfacing the fetch error from the data loader (add/propagate a
sessionsError or sessionsFetchError from the API call that populates sessions)
and change the conditional in the history panel render (the block using
sessionsLoading and sessions?.length) to first check for sessionsError and
render an explicit error state (message and retry action) instead of the
empty-state UI; update the same pattern at the other locations you noted (around
the blocks referencing sessionsLoading/sessions at ~317-321, ~421-429, ~556-560)
so error vs empty vs loading are distinct and the resource drawer doesn't fall
back to "Upload documents to begin" on fetch failure.
- Around line 274-291: Icon-only Buttons like the ones rendering Edit2/Trash2
lack accessible names; update each Button (e.g., the one calling
startRenamingSession(sess.id, sess.title) and the delete handler using
deleteSessionMutation.mutate(sess.id)) to provide an accessible name by adding
an aria-label (or aria-labelledby) or include visually hidden text
(screen-reader-only span) describing the action (e.g., "Rename conversation" and
"Delete conversation"); apply the same pattern to other icon-only controls
mentioned (rename/delete/external-link/collapse/send) so all icon buttons expose
clear, unique labels to assistive technologies.
In `@frontend/vite.config.ts`:
- Around line 4-15: The Vite config uses CommonJS __dirname which breaks under
ESM; update the top of the config to derive a directory from import.meta.url
(e.g. use path.dirname(fileURLToPath(import.meta.url))) before using
path.resolve in the resolve.alias entry so the alias "@": path.resolve(...) uses
the ESM-compatible directory value; add the required import from 'url'
(fileURLToPath) and reference the derived __dirname replacement when creating
the alias in defineConfig (affects resolve.alias and any other places relying on
__dirname).
---
Outside diff comments:
In `@frontend/src/pages/Resources.tsx`:
- Around line 82-85: The onSuccess handler currently calls setFile(null) which
only clears React state but leaves the native file input value intact; add a ref
to the file input (e.g., fileInputRef) and in the onSuccess handlers (the one
shown and the other at lines 222-227) set fileInputRef.current.value = '' (or
otherwise reset the input element) in addition to setFile(null), so the browser
will allow selecting the same file again.
---
Duplicate comments:
In `@frontend/src/components/ui/scroll-area.tsx`:
- Line 4: The import path for ScrollArea is incorrect—replace the non-standard
import from "radix-ui" with the official package import so the ScrollArea
primitive resolves correctly; update the import that brings in ScrollArea as
ScrollAreaPrimitive (used in this file) to import from
"`@radix-ui/react-scroll-area`" so subsequent usages of ScrollAreaPrimitive work
without module resolution errors.
In `@frontend/src/components/ui/select.tsx`:
- Line 2: The import in select.tsx uses an incorrect package path "radix-ui";
update the import to the proper Radix package (e.g., change `import { Select as
SelectPrimitive } from "radix-ui"` to import from `@radix-ui/react-select`) so
the `Select` (aliased as `SelectPrimitive`) resolves correctly; ensure the named
import remains `Select as SelectPrimitive` and adjust any other import variants
in this file to the same scoped package.
---
Nitpick comments:
In `@frontend/src/App.tsx`:
- Line 16: Remove the unused import SidebarProvider from the top of the file;
keep TooltipProvider which is actually used to wrap the application routes.
Locate the import statement that includes SidebarProvider (the one that also
imports from "`@/components/ui/sidebar`") and delete SidebarProvider from that
import so only TooltipProvider (and any other used symbols) remain.
- Around line 109-115: The hardcoded quota value 30 is used in the progress
width and display; update the component to read a quota property (e.g.,
displayUser?.quota or displayUser?.questions_quota) and use that for both the
Math.min((displayUser?.questions_used || 0) / quota * 100, 100) calculation and
the displayed string "Quota: X / Y", with a sensible fallback (e.g., 30) if the
quota field is missing; modify references around displayUser and questions_used
in App.tsx so both progress bar style and the span use the same quota variable.
In `@frontend/src/components/AppSidebar.tsx`:
- Around line 73-92: The active-state check uses exact equality
(location.pathname === item.path) so parent nav items in navItems (e.g.,
"Resources") won't be highlighted for nested routes like /resources/123; update
the SidebarMenuButton isActive logic to consider prefix matching (e.g.,
location.pathname.startsWith(item.path)) or a more robust helper that treats "/"
specially and avoids false positives for similarly prefixed paths; change the
comparison where SidebarMenuButton is rendered (the location.pathname ===
item.path expression) to this startsWith-based check and adjust the className
conditional to use the same helper.
- Around line 39-49: Initialize and persist the dark-mode preference: change the
isDark state initialization to read from localStorage (fallback to
document.documentElement.classList.contains('dark') or a system preference) and
update localStorage whenever isDark changes; specifically update the useState
initializer for isDark and add/localize the side-effect in the useEffect that
runs on [isDark] to both add/remove the 'dark' class on document.documentElement
and call localStorage.setItem('theme' or similar, isDark ? 'dark' : 'light'),
keeping the existing class-add/remove logic in the effect and keeping setIsDark
usage unchanged.
In `@frontend/src/components/ui/alert-dialog.tsx`:
- Around line 58-60: The class list in alert-dialog.tsx contains redundant size
variants (data-[size=default]:max-w-xs and data-[size=sm]:max-w-xs) that both
set the same max-width; update the className passed to cn inside the
alert-dialog component to remove the duplicate data-[size=sm]:max-w-xs (or
replace both with a single max-w-xs) so the element uses a single, explicit
max-width rule; locate the string passed to cn in the alert dialog's JSX (the
group/alert-dialog-content class block) and simplify the size-related classes
accordingly.
In `@frontend/src/pages/Login.tsx`:
- Around line 87-94: Add ARIA attributes to the email (and similarly the
password) input to expose validation state to assistive tech: set aria-invalid
to Boolean(errors.email) and set aria-describedby to the id of the error message
(e.g., `${id}-error`), and ensure the error <p> has that same id (e.g.,
"email-error") so screen readers announce it; update the JSX for the Input using
register('email') and the error <p> (errors.email) accordingly and mirror the
same changes for the password input and its error paragraph.
In `@frontend/src/pages/Register.tsx`:
- Around line 85-92: Add ARIA attributes to form inputs to improve screen-reader
validation feedback: for the email Input (where register('email') is used and
errors.email is checked) set aria-invalid={!!errors.email} and aria-describedby
to the id of the error paragraph (e.g., "email-error") when an error exists, and
give the error <p> that id; repeat the same pattern for the password and
confirmPassword Inputs (where register('password') and
register('confirmPassword') are used and errors.password /
errors.confirmPassword are checked) so each input has aria-invalid and
aria-describedby pointing to its corresponding error element.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5fbb3fdf-f843-451a-bb1f-e38523332b76
⛔ Files ignored due to path filters (8)
frontend/bun.lockis excluded by!**/*.lockfrontend/i1.pngis excluded by!**/*.pngfrontend/i2.pngis excluded by!**/*.pngfrontend/i3.pngis excluded by!**/*.pngfrontend/i4.pngis excluded by!**/*.pngfrontend/i5.pngis excluded by!**/*.pngfrontend/i6.pngis excluded by!**/*.pngfrontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (40)
.geminiignoreDEPLOY.mdfrontend/.gitignorefrontend/Dockerfilefrontend/components.jsonfrontend/nginx.conffrontend/package.jsonfrontend/src/App.tsxfrontend/src/components/AppSidebar.tsxfrontend/src/components/Layout.tsxfrontend/src/components/ui/alert-dialog.tsxfrontend/src/components/ui/avatar.tsxfrontend/src/components/ui/badge.tsxfrontend/src/components/ui/button.tsxfrontend/src/components/ui/card.tsxfrontend/src/components/ui/dialog.tsxfrontend/src/components/ui/dropdown-menu.tsxfrontend/src/components/ui/input.tsxfrontend/src/components/ui/label.tsxfrontend/src/components/ui/progress.tsxfrontend/src/components/ui/scroll-area.tsxfrontend/src/components/ui/select.tsxfrontend/src/components/ui/separator.tsxfrontend/src/components/ui/sheet.tsxfrontend/src/components/ui/sidebar.tsxfrontend/src/components/ui/skeleton.tsxfrontend/src/components/ui/table.tsxfrontend/src/components/ui/tabs.tsxfrontend/src/components/ui/tooltip.tsxfrontend/src/hooks/use-mobile.tsfrontend/src/index.cssfrontend/src/lib/utils.tsfrontend/src/pages/Login.tsxfrontend/src/pages/Register.tsxfrontend/src/pages/Resources.tsxfrontend/src/pages/Solver.tsxfrontend/tsconfig.app.jsonfrontend/tsconfig.jsonfrontend/vite.config.tstodo.md
| FROM nginx:stable-alpine | ||
| COPY --from=build /app/dist /usr/share/nginx/html | ||
| # Default nginx config is usually fine for SPAs, | ||
| # but for React Router you might need a custom one to handle 404s | ||
| COPY nginx.conf /etc/nginx/conf.d/default.conf | ||
| EXPOSE 80 | ||
| CMD ["nginx", "-g", "daemon off;"] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify runtime user and privileged port usage in container config.
rg -n '^\s*FROM\s+nginx|^\s*USER\b|^\s*EXPOSE\b' frontend/Dockerfile
rg -n '^\s*listen\s+' frontend/nginx.confRepository: shubhamxdd/pyq-appl
Length of output: 121
🏁 Script executed:
# Check for deployment configs that might reference the frontend service
fd -e 'docker-compose.yml' -e 'docker-compose.yaml' -e '.yaml' -e '.yml' | head -20Repository: shubhamxdd/pyq-appl
Length of output: 82
🏁 Script executed:
# Check if there are any other references to port 80 in frontend-related configs
rg -i 'port.*80|80.*port' --type yaml --type json frontend/ 2>/dev/null || rg -i 'port.*80|80.*port' . --type yaml --type json 2>/dev/null | head -20Repository: shubhamxdd/pyq-appl
Length of output: 154
🏁 Script executed:
# Verify the nginx user exists in the alpine image (standard practice)
rg -n '^\s*USER\s' frontend/DockerfileRepository: shubhamxdd/pyq-appl
Length of output: 45
🏁 Script executed:
cat docker-compose.ymlRepository: shubhamxdd/pyq-appl
Length of output: 1460
🏁 Script executed:
# Check for any Kubernetes, Terraform, or other deployment configs
fd -type f \( -name '*.yaml' -o -name '*.yml' -o -name '*.tf' -o -name '*.json' \) | xargs rg -l 'frontend|nginx' 2>/dev/null | head -20Repository: shubhamxdd/pyq-appl
Length of output: 486
🏁 Script executed:
# Verify nginx:stable-alpine image includes nginx user (standard in official images)
# and check frontend/nginx.conf for any other directives that might conflict
cat -n frontend/nginx.confRepository: shubhamxdd/pyq-appl
Length of output: 471
Run the runtime image as non-root.
Lines 12–18 leave the Nginx container running as root, which weakens container isolation in production.
Suggested hardening patch
FROM nginx:stable-alpine
COPY --from=build /app/dist /usr/share/nginx/html
# Default nginx config is usually fine for SPAs,
# but for React Router you might need a custom one to handle 404s
COPY nginx.conf /etc/nginx/conf.d/default.conf
-EXPOSE 80
+USER nginx
+EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]Also update frontend/nginx.conf:
listen 8080;📝 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.
| FROM nginx:stable-alpine | |
| COPY --from=build /app/dist /usr/share/nginx/html | |
| # Default nginx config is usually fine for SPAs, | |
| # but for React Router you might need a custom one to handle 404s | |
| COPY nginx.conf /etc/nginx/conf.d/default.conf | |
| EXPOSE 80 | |
| CMD ["nginx", "-g", "daemon off;"] | |
| FROM nginx:stable-alpine | |
| COPY --from=build /app/dist /usr/share/nginx/html | |
| # Default nginx config is usually fine for SPAs, | |
| # but for React Router you might need a custom one to handle 404s | |
| COPY nginx.conf /etc/nginx/conf.d/default.conf | |
| USER nginx | |
| EXPOSE 8080 | |
| CMD ["nginx", "-g", "daemon off;"] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/Dockerfile` around lines 12 - 18, The container runs Nginx as root;
modify the Dockerfile to create or use a non-root user and switch to it (e.g.,
create a user/group or use the existing nginx user) after copying assets and set
proper ownership of /usr/share/nginx/html so Nginx can serve files without root;
also align the exposed port with the updated nginx.conf (change EXPOSE from 80
to 8080) and keep CMD ["nginx", "-g", "daemon off;"] (nginx.conf will bind to
8080), ensuring USER is set after the files are owned by the non-root user and
before the final CMD.
| "react-hot-toast": "^2.6.0", | ||
| "react-markdown": "^10.1.0", | ||
| "react-router-dom": "^7.15.1", | ||
| "shadcn": "^4.7.0", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether `shadcn` is referenced by runtime source files.
rg -nP --type=ts --type=tsx --type=js --type=jsx \
'(from\s+["'"'"']shadcn["'"'"']|require\(["'"'"']shadcn["'"'"']\))' frontend/src frontendRepository: shubhamxdd/pyq-appl
Length of output: 91
🏁 Script executed:
# Search for shadcn imports in a simpler way
rg -i 'shadcn' frontend/src frontend --max-count=20Repository: shubhamxdd/pyq-appl
Length of output: 1951
🏁 Script executed:
# Check the frontend package.json for shadcn usage in scripts
cat -n frontend/package.json | head -50Repository: shubhamxdd/pyq-appl
Length of output: 1780
Move shadcn to devDependencies—it's a CLI tool with no runtime imports.
shadcn is listed on line 27 as a runtime dependency but has no imports in application code. The bun.lock indicates it provides a CLI executable (bin: { "shadcn": "dist/index.js" }) used for component generation, not runtime functionality. Moving it to devDependencies reduces the production install surface without impact.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/package.json` at line 27, The package.json currently lists "shadcn"
as a runtime dependency; move it into devDependencies instead. Edit package.json
to remove the "shadcn": "^4.7.0" entry from the top-level "dependencies" object
and add the same "shadcn": "^4.7.0" entry under "devDependencies" so it remains
available for CLI/component generation but is not installed in production.
| {showCloseButton && ( | ||
| <DialogPrimitive.Close data-slot="dialog-close" asChild> | ||
| <Button | ||
| variant="ghost" | ||
| className="absolute top-2 right-2" | ||
| size="icon-sm" | ||
| > | ||
| <XIcon | ||
| /> | ||
| <span className="sr-only">Close</span> | ||
| </Button> | ||
| </DialogPrimitive.Close> | ||
| )} |
There was a problem hiding this comment.
Add explicit size to XIcon.
The XIcon defaults to 24×24px but the button uses size="icon-sm", which typically expects a 16×16px icon. Add a size or className prop to the icon for proper visual hierarchy.
🎨 Suggested fix
<Button
variant="ghost"
className="absolute top-2 right-2"
size="icon-sm"
>
- <XIcon
- />
+ <XIcon className="size-4" />
<span className="sr-only">Close</span>
</Button>📝 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.
| {showCloseButton && ( | |
| <DialogPrimitive.Close data-slot="dialog-close" asChild> | |
| <Button | |
| variant="ghost" | |
| className="absolute top-2 right-2" | |
| size="icon-sm" | |
| > | |
| <XIcon | |
| /> | |
| <span className="sr-only">Close</span> | |
| </Button> | |
| </DialogPrimitive.Close> | |
| )} | |
| {showCloseButton && ( | |
| <DialogPrimitive.Close data-slot="dialog-close" asChild> | |
| <Button | |
| variant="ghost" | |
| className="absolute top-2 right-2" | |
| size="icon-sm" | |
| > | |
| <XIcon className="size-4" /> | |
| <span className="sr-only">Close</span> | |
| </Button> | |
| </DialogPrimitive.Close> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/ui/dialog.tsx` around lines 70 - 82, The close icon
currently uses the default 24×24px size while the surrounding Button uses
size="icon-sm" (16×16px); update the XIcon inside the
DialogPrimitive.Close/Button to explicitly match the button by passing a size or
a className (e.g., size={16} or className="w-4 h-4") so the icon scales to the
icon-sm button—locate the XIcon in the showCloseButton block within
DialogPrimitive.Close and adjust its props accordingly.
| {res.status === 'failed' && ( | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="size-9 text-blue-600 hover:text-blue-700 hover:bg-blue-50 dark:hover:bg-blue-900/20" | ||
| onClick={() => retryMutation.mutate(res.id)} | ||
| disabled={retryMutation.isPending} | ||
| > | ||
| <RefreshCw className={cn("size-4", retryMutation.isPending && "animate-spin")} /> | ||
| </Button> |
There was a problem hiding this comment.
Track retry state per resource instead of globally.
retryMutation.isPending is shared across the whole table, so retrying one failed document disables and spins every failed row at once.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Resources.tsx` around lines 379 - 388, The current UI uses
a single retryMutation.isPending for all rows, causing every failed row to
disable/animate when one retry runs; change to track pending retries per
resource (by id) instead: create a local state map/set (e.g., pendingRetryIds)
and update it in the retry flow (add id on mutate/start and remove on
success/error/settle) or use per-id mutations so each call has its own
isPending; then replace references to retryMutation.isPending with
pendingRetryIds.has(res.id) (and use that same check to add the "animate-spin"
class to <RefreshCw />) and ensure you update this set inside retryMutation's
onMutate/onSettled (or create a per-row useMutation that calls the same retry
API with res.id) so only the clicked row shows disabled/spinning.
| <Button | ||
| onClick={() => createSessionMutation.mutate()} | ||
| className="w-full rounded-xl shadow-sm h-11" | ||
| variant="default" | ||
| > | ||
| <Plus className="size-4 mr-2" /> | ||
| New Session | ||
| </Button> |
There was a problem hiding this comment.
Guard the session-creation buttons while the request is in flight.
All three entry points stay clickable during session creation, so rapid taps can create multiple empty sessions.
Suggested fix
<Button
- onClick={() => createSessionMutation.mutate()}
+ onClick={() => {
+ if (!createSessionMutation.isPending) createSessionMutation.mutate();
+ }}
+ disabled={createSessionMutation.isPending}
className="w-full rounded-xl shadow-sm h-11"
variant="default"
>Apply the same guard to the collapsed-sidebar and desktop-sidebar variants too.
Also applies to: 385-390, 397-404
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Solver.tsx` around lines 216 - 223, The three "New
Session" Buttons (the one shown and the collapsed-sidebar and desktop-sidebar
variants referenced around lines 385-390 and 397-404) are not guarded against
duplicate clicks; update each Button that calls createSessionMutation.mutate()
to be disabled while the mutation is in flight by using
createSessionMutation.isLoading (or isLoading/isMutating from your mutation
hook) as the disabled prop (and optionally adjust className/aria-busy). Ensure
you apply the same change to the Button instances that reference
createSessionMutation to prevent multiple rapid session creations.
| {sessionsLoading ? ( | ||
| <div className="flex justify-center py-8"><Loader2 className="w-6 h-6 animate-spin text-gray-300" /></div> | ||
| <div className="space-y-2 p-3"> | ||
| {[1, 2, 3].map(i => <div key={i} className="h-10 bg-muted/40 animate-pulse rounded-lg" />)} | ||
| </div> | ||
| ) : sessions?.length === 0 ? ( | ||
| <p className="text-center py-8 text-sm text-gray-400 italic">No history yet.</p> | ||
| <div className="py-12 text-center space-y-2 px-6"> | ||
| <MessageSquare className="size-8 mx-auto opacity-10" /> | ||
| <p className="text-xs text-muted-foreground">Your chat history will appear here.</p> | ||
| </div> |
There was a problem hiding this comment.
Handle failed fetches separately from empty-state UI.
These new branches only distinguish loading vs. “no data”. When a query fails, the history panel goes blank and the resource drawer falls back to “Upload documents to begin,” which is misleading if the user already has data server-side.
Also applies to: 317-321, 421-429, 556-560
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Solver.tsx` around lines 232 - 240, The UI treats failed
fetches the same as "no data" because the render logic only checks
sessionsLoading and sessions?.length; fix this by surfacing the fetch error from
the data loader (add/propagate a sessionsError or sessionsFetchError from the
API call that populates sessions) and change the conditional in the history
panel render (the block using sessionsLoading and sessions?.length) to first
check for sessionsError and render an explicit error state (message and retry
action) instead of the empty-state UI; update the same pattern at the other
locations you noted (around the blocks referencing sessionsLoading/sessions at
~317-321, ~421-429, ~556-560) so error vs empty vs loading are distinct and the
resource drawer doesn't fall back to "Upload documents to begin" on fetch
failure.
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="size-7 text-muted-foreground hover:text-primary" | ||
| onClick={() => startRenamingSession(sess.id, sess.title)} | ||
| > | ||
| <MessageSquare className="w-4 h-4 mt-0.5 flex-shrink-0 opacity-70" /> | ||
| <span className="truncate pr-12">{sess.title}</span> | ||
| </button> | ||
| <div className="absolute right-2 top-1/2 -translate-y-1/2 flex items-center opacity-0 group-hover:opacity-100 transition-opacity"> | ||
| <button | ||
| onClick={() => startRenamingSession(sess.id, sess.title)} | ||
| className="p-1.5 text-gray-400 hover:text-blue-500" | ||
| > | ||
| <Edit2 className="w-3.5 h-3.5" /> | ||
| </button> | ||
| <button | ||
| onClick={(e) => { e.stopPropagation(); if(window.confirm('Delete chat?')) deleteSessionMutation.mutate(sess.id); }} | ||
| className="p-1.5 text-gray-400 hover:text-red-500" | ||
| > | ||
| <Trash2 className="w-3.5 h-3.5" /> | ||
| </button> | ||
| </div> | ||
| </> | ||
| <Edit2 className="size-3" /> | ||
| </Button> | ||
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| className="size-7 text-muted-foreground hover:text-destructive" | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| if(window.confirm('Delete this conversation?')) deleteSessionMutation.mutate(sess.id); | ||
| }} | ||
| > | ||
| <Trash2 className="size-3" /> |
There was a problem hiding this comment.
Add accessible names to the icon-only controls on this page.
History actions, sheet triggers, sidebar toggles, external-link buttons, and the send button are all icon-only here, so screen readers do not get a usable action name.
Suggested fix
- <Button variant="ghost" size="icon" className="md:hidden shrink-0">
+ <Button variant="ghost" size="icon" className="md:hidden shrink-0" aria-label="Open chat history">
<Menu className="size-5" />
</Button>
...
- <Button variant="ghost" size="icon" className="hidden md:flex" onClick={() => setShowContext(!showContext)}>
+ <Button
+ variant="ghost"
+ size="icon"
+ className="hidden md:flex"
+ aria-label={showContext ? "Hide resource context" : "Show resource context"}
+ onClick={() => setShowContext(!showContext)}
+ >
<PanelRight className={cn("size-5 transition-colors", showContext ? "text-primary" : "text-muted-foreground")} />
</Button>
...
- <Button
+ <Button
type="submit"
+ aria-label="Send message"
disabled={isStreaming || !question.trim() || selectedResources.length === 0}
className="absolute right-2 bottom-2 md:right-3 md:bottom-3 size-9 md:size-10 rounded-xl shadow-lg transition-transform active:scale-90"
>Please apply the same pattern to rename/delete/external-link/collapse actions as well.
Also applies to: 348-357, 385-412, 463-480, 502-504, 529-538, 639-645
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/pages/Solver.tsx` around lines 274 - 291, Icon-only Buttons like
the ones rendering Edit2/Trash2 lack accessible names; update each Button (e.g.,
the one calling startRenamingSession(sess.id, sess.title) and the delete handler
using deleteSessionMutation.mutate(sess.id)) to provide an accessible name by
adding an aria-label (or aria-labelledby) or include visually hidden text
(screen-reader-only span) describing the action (e.g., "Rename conversation" and
"Delete conversation"); apply the same pattern to other icon-only controls
mentioned (rename/delete/external-link/collapse/send) so all icon buttons expose
clear, unique labels to assistive technologies.
| import path from "path" | ||
|
|
||
| // https://vite.dev/config/ | ||
| export default defineConfig({ | ||
| plugins: [ | ||
| react(), | ||
| tailwindcss(), | ||
| ], | ||
| resolve: { | ||
| alias: { | ||
| "@": path.resolve(__dirname, "./src"), | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify ESM mode and __dirname usage in Vite config
rg -n '"type"\s*:\s*"module"' frontend/package.json
rg -n '\b__dirname\b' frontend/vite.config.tsRepository: shubhamxdd/pyq-appl
Length of output: 133
🏁 Script executed:
#!/bin/bash
# Check the entire vite.config.ts file for context
cat -n frontend/vite.config.ts
# Verify no other __dirname or path module usages
rg -n '__dirname|path\.' frontend/vite.config.tsRepository: shubhamxdd/pyq-appl
Length of output: 570
Fix ESM-incompatible __dirname usage in Vite config.
Line 14 uses __dirname, which is not available in ESM modules ("type": "module"). This will fail at startup when Vite loads the config.
Suggested fix
-import path from "path"
+import { fileURLToPath, URL } from "node:url"
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
resolve: {
alias: {
- "@": path.resolve(__dirname, "./src"),
+ "@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
})📝 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 path from "path" | |
| // https://vite.dev/config/ | |
| export default defineConfig({ | |
| plugins: [ | |
| react(), | |
| tailwindcss(), | |
| ], | |
| resolve: { | |
| alias: { | |
| "@": path.resolve(__dirname, "./src"), | |
| }, | |
| import { fileURLToPath, URL } from "node:url" | |
| // https://vite.dev/config/ | |
| export default defineConfig({ | |
| plugins: [ | |
| react(), | |
| tailwindcss(), | |
| ], | |
| resolve: { | |
| alias: { | |
| "@": fileURLToPath(new URL("./src", import.meta.url)), | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/vite.config.ts` around lines 4 - 15, The Vite config uses CommonJS
__dirname which breaks under ESM; update the top of the config to derive a
directory from import.meta.url (e.g. use
path.dirname(fileURLToPath(import.meta.url))) before using path.resolve in the
resolve.alias entry so the alias "@": path.resolve(...) uses the ESM-compatible
directory value; add the required import from 'url' (fileURLToPath) and
reference the derived __dirname replacement when creating the alias in
defineConfig (affects resolve.alias and any other places relying on __dirname).
in this pr i have made mostly changes on frontend, using shadcn/ui
Summary by CodeRabbit
Release Notes
New Features
Documentation