feat: directory picker UI -- select packages before indexing monorepos (OPE-110)#268
Conversation
…(OPE-111) New component: DirectoryPicker.tsx (252 lines) - Modal with card grid for monorepo package selection - Cards sized proportionally to file count (120-240px) - Selected: primary border + glow. Deselected: muted/ghost - Click to toggle, Select All/Deselect All checkbox - Header: owner/repo with file + function counts - Footer: selection summary + Clone & Index / Cancel buttons - Uses shadcn Card, Button, Checkbox, ScrollArea New: checkbox.tsx (shadcn) + @radix-ui/react-checkbox New types: DirectoryEntry, AnalyzeResult in types.ts Build verified: bun run build passes
BudgetBar component shows estimated functions vs tier limit: - Green bar: under 70% of limit - Amber bar: 70-100% of limit - Red bar: over limit, with 'Over your plan limit by X functions' text - Smooth CSS transition on width changes (300ms ease-out) - Counter shows 'current / limit' with red text when over PickerFooter updated: - Disabled 'Clone & Index' button when over limit - Button turns destructive/red with 'Over plan limit' text - Dynamic button label based on state (loading/empty/over/ready) Tier limits added to config/api.ts: free=2K, pro=20K, enterprise=500K 305 lines total (5 sub-components, logically cohesive). Build passes.
…113) Cards now appear one-by-one with staggered fade+slide (40ms per card): - framer-motion staggerChildren on card grid container - Each card: opacity 0->1, y 8->0 on mount - Total stagger for 33 packages: ~1.3s (feels deliberate, not slow) Card hover polish: - Subtle scale-up (1.02x) on hover - Shadow lift on deselected cards - 200ms transition for snappy feel 320 lines. Build passes.
…flow (OPE-114) AddRepoForm.tsx: - On submit, calls POST /repos/analyze for GitHub URLs - If large repo (suggestion: 'large_repo'), calls onAnalyzed callback - If small repo or non-GitHub, proceeds directly to clone+index - Button shows 'Analyzing...' state with pulse icon during API call - Graceful fallback: if analyze fails, falls through to direct add DashboardHome.tsx: - New state: analyzeResult, pendingGitUrl/Branch, showDirectoryPicker - handleAnalyzed: opens DirectoryPicker with analyze result - handleDirectoryConfirm: calls addAndIndex with include_paths - addAndIndex sends IndexConfig body when include_paths provided - DirectoryPicker wired with TIER_FUNCTION_LIMITS.free as budget limit Flow: URL -> Analyze -> Picker (large) -> Clone -> Index (subset) or: URL -> Analyze -> Clone -> Index (small, no picker) Build passes. 194 + 295 + 320 lines across 3 files.
|
@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Rate limit exceeded
⌛ 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. 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAddRepoForm can analyze GitHub repos and surface a DirectoryPicker for monorepo subset selection; DashboardHome coordinates analysis results and subset indexing (include_paths). New types, TIER_FUNCTION_LIMITS, a Radix Checkbox UI component, and a package dependency were added. Changes
Sequence DiagramsequenceDiagram
participant User as "User"
participant AddForm as "AddRepoForm\n(UI)"
participant API as "Backend API"
participant Dashboard as "DashboardHome"
participant Picker as "DirectoryPicker"
User->>AddForm: Submit GitHub URL
AddForm->>API: POST /repos/analyze (github_url)
API-->>AddForm: AnalyzeResult (directories, stats)
AddForm->>Dashboard: onAnalyzed(result, gitUrl, branch)
Dashboard->>Picker: render with AnalyzeResult & limits
User->>Picker: select directories & confirm
Picker-->>Dashboard: onConfirm(selectedPaths)
Dashboard->>API: POST /repos/add (git_url + include_paths)
API-->>Dashboard: Success/Failure
Dashboard->>User: show indexing modal / error toast
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/AddRepoForm.tsx (1)
93-95:⚠️ Potential issue | 🟡 MinorPrevent closing the modal while analysis is running.
The backdrop close guard checks
loadingonly, so users can dismiss the form duringanalyzing.💡 Suggested fix
- onClick={() => !loading && setShowForm(false)} + onClick={() => !isBusy && setShowForm(false)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/AddRepoForm.tsx` around lines 93 - 95, The backdrop onClick currently only guards against `loading`, so users can still close the modal during analysis; update the handler used in the container (the onClick that calls setShowForm) to check both `loading` and the analysis state (e.g., `analyzing`) before closing—e.g., change the condition in the onClick from `!loading && setShowForm(false)` to `!loading && !analyzing && setShowForm(false)` (or use a combined `isBusy` flag) so the modal cannot be dismissed while analysis is running.
🧹 Nitpick comments (1)
frontend/src/config/api.ts (1)
71-71: MoveTierNameto the shared types module.
TierNameis a shared exported type and should live infrontend/src/types.tsto keep type ownership consistent across the app.As per coding guidelines "Shared types go in
frontend/src/types.ts".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/config/api.ts` at line 71, Move the exported type alias TierName (currently defined as "type TierName = keyof typeof TIER_FUNCTION_LIMITS") out of the config module and into the shared types module (frontend/src/types.ts): add an exported declaration for TierName in types.ts and remove the duplicate from frontend/src/config/api.ts, then update any files importing TierName to import it from the shared types module instead; keep TIER_FUNCTION_LIMITS where it belongs and ensure the new types.ts export references the same symbol name (TIER_FUNCTION_LIMITS) or, if that constant is not accessible from types.ts, change the exported type to a string union or re-export TIER_FUNCTION_LIMITS from the config module so TierName can be derived centrally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/AddRepoForm.tsx`:
- Around line 114-120: The icon-only close button (the element that calls
setShowForm(false) and uses isBusy and the X icon) lacks an accessible name; add
an accessible label by adding aria-label="Close" (or aria-label with localized
text) to the button or include a visually hidden text node (e.g., a span with
className="sr-only" containing "Close") so screen readers can announce the
control, keeping the disabled={isBusy} and existing classes intact.
In `@frontend/src/components/dashboard/DashboardHome.tsx`:
- Around line 252-260: The DirectoryPicker is being passed a hard-coded
free-tier limit (TIER_FUNCTION_LIMITS.free); change it to use the actual
authenticated user tier by passing TIER_FUNCTION_LIMITS[userTier] (or
TIER_FUNCTION_LIMITS[effectiveTier] if you compute a fallback) and ensure the
component has access to the userTier variable (or derive it from existing
auth/context state before the DirectoryPicker render), falling back to 'free'
when userTier is undefined; keep the other props (isOpen, onClose,
repoInfo/analyzeResult, onConfirm/handleDirectoryConfirm, loading) unchanged.
In `@frontend/src/components/DirectoryPicker.tsx`:
- Around line 187-193: The header close button in DirectoryPicker is icon-only
and missing an accessible name; update the button that uses the onClose handler
and the X icon to include an accessible label (e.g., add aria-label="Close" or
aria-label={`Close ${directoryName || 'panel'}`} or include visually hidden
text) so screen readers can announce the action while preserving the existing
styling and disabled logic.
- Around line 18-24: The DirectoryPicker component's props interface is missing
the functionLimit property and the modal close button lacks an accessible name:
update the DirectoryPickerProps interface to include functionLimit: number (or
the correct type used by DirectoryPicker) so the destructured functionLimit in
the DirectoryPicker component matches the props, and add aria-label="Close" to
the close button element rendered by DirectoryPicker to provide an accessible
name; reference the DirectoryPickerProps interface and the DirectoryPicker
component (where functionLimit is destructured and the close button is rendered)
when making these changes.
---
Outside diff comments:
In `@frontend/src/components/AddRepoForm.tsx`:
- Around line 93-95: The backdrop onClick currently only guards against
`loading`, so users can still close the modal during analysis; update the
handler used in the container (the onClick that calls setShowForm) to check both
`loading` and the analysis state (e.g., `analyzing`) before closing—e.g., change
the condition in the onClick from `!loading && setShowForm(false)` to `!loading
&& !analyzing && setShowForm(false)` (or use a combined `isBusy` flag) so the
modal cannot be dismissed while analysis is running.
---
Nitpick comments:
In `@frontend/src/config/api.ts`:
- Line 71: Move the exported type alias TierName (currently defined as "type
TierName = keyof typeof TIER_FUNCTION_LIMITS") out of the config module and into
the shared types module (frontend/src/types.ts): add an exported declaration for
TierName in types.ts and remove the duplicate from frontend/src/config/api.ts,
then update any files importing TierName to import it from the shared types
module instead; keep TIER_FUNCTION_LIMITS where it belongs and ensure the new
types.ts export references the same symbol name (TIER_FUNCTION_LIMITS) or, if
that constant is not accessible from types.ts, change the exported type to a
string union or re-export TIER_FUNCTION_LIMITS from the config module so
TierName can be derived centrally.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
frontend/package.jsonfrontend/src/components/AddRepoForm.tsxfrontend/src/components/DirectoryPicker.tsxfrontend/src/components/dashboard/DashboardHome.tsxfrontend/src/components/ui/checkbox.tsxfrontend/src/config/api.tsfrontend/src/types.ts
Add min-h-0 to ScrollArea -- flex children default to min-height:auto which prevents them from shrinking below content size. Without min-h-0, the card grid grows to fit all content and never triggers overflow scroll. Classic flexbox scroll fix.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
frontend/src/components/DirectoryPicker.tsx (2)
18-33:⚠️ Potential issue | 🔴 Critical
functionLimitis still missing fromDirectoryPickerProps(TS type break).Line 32 and Lines 137-144 use
functionLimit, but Line 18-24 does not declare it inDirectoryPickerProps, which will fail type-checking.🛠️ Proposed fix
interface DirectoryPickerProps { isOpen: boolean onClose: () => void repoInfo: AnalyzeResult onConfirm: (selectedPaths: string[]) => void loading: boolean + functionLimit?: number }#!/bin/bash # Verify that functionLimit is destructured/used but missing in DirectoryPickerProps. sed -n '18,40p' frontend/src/components/DirectoryPicker.tsx echo "----" rg -n -C2 'functionLimit|interface DirectoryPickerProps' frontend/src/components/DirectoryPicker.tsxAlso applies to: 137-144
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/DirectoryPicker.tsx` around lines 18 - 33, The DirectoryPickerProps interface is missing the functionLimit property used by the DirectoryPicker component; add functionLimit: number (or the correct type) to DirectoryPickerProps so the prop is typed, and ensure the DirectoryPicker function signature still destructures functionLimit. Update the interface declaration (DirectoryPickerProps) to include functionLimit and verify usages in DirectoryPicker and related code paths (lines referencing functionLimit) compile with the chosen type.
187-193:⚠️ Potential issue | 🟠 MajorAdd an accessible name to the icon-only close button.
Line 187’s button still has no accessible label, so screen readers won’t announce the action clearly.
♿ Suggested fix
<button + type="button" + aria-label="Close directory picker" onClick={onClose} disabled={loading} className="w-8 h-8 flex items-center justify-center rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors" >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/DirectoryPicker.tsx` around lines 187 - 193, The close button in DirectoryPicker (the button with onClick={onClose} rendering the <X /> icon) lacks an accessible name; add an explicit accessible label by including an aria-label (e.g., aria-label="Close") or aria-labelledby that references a visible/visually-hidden text node so screen readers announce the action; update the button element in the DirectoryPicker component to include this attribute while keeping existing props (onClick, disabled, className) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/DirectoryPicker.tsx`:
- Around line 34-50: Selected paths can become stale when repoInfo.directories
changes; before calling onConfirm (where Array.from(selected) is sent)
filter/prune the selected Set against the current repoInfo.directories to only
include paths that still exist. Implement a small utility or useEffect in
DirectoryPicker that, when repoInfo.directories changes, creates a new Set of
only those entries present in repoInfo.directories (use the same path keys as
used in stats), update setSelected with the pruned Set, and in the onConfirm
handler send Array.from(prunedSelected) (or compute currentSelected locally)
instead of the raw selected to ensure no stale paths are submitted.
---
Duplicate comments:
In `@frontend/src/components/DirectoryPicker.tsx`:
- Around line 18-33: The DirectoryPickerProps interface is missing the
functionLimit property used by the DirectoryPicker component; add functionLimit:
number (or the correct type) to DirectoryPickerProps so the prop is typed, and
ensure the DirectoryPicker function signature still destructures functionLimit.
Update the interface declaration (DirectoryPickerProps) to include functionLimit
and verify usages in DirectoryPicker and related code paths (lines referencing
functionLimit) compile with the chosen type.
- Around line 187-193: The close button in DirectoryPicker (the button with
onClick={onClose} rendering the <X /> icon) lacks an accessible name; add an
explicit accessible label by including an aria-label (e.g., aria-label="Close")
or aria-labelledby that references a visible/visually-hidden text node so screen
readers announce the action; update the button element in the DirectoryPicker
component to include this attribute while keeping existing props (onClick,
disabled, className) unchanged.
5 findings verified and fixed: 1. AddRepoForm close button: add aria-label='Close' for screen readers 2. AddRepoForm backdrop: use isBusy (loading || analyzing) guard instead of just loading -- prevents dismissal during analyze API call 3. DirectoryPicker close button: add aria-label='Close' 4. DirectoryPicker props: add functionLimit to interface (was destructured but missing from type definition) 5. DashboardHome: derive userTier from session metadata with 'free' fallback instead of hardcoded TIER_FUNCTION_LIMITS.free Skipped: TierName move to types.ts -- type derives from TIER_FUNCTION_LIMITS via keyof typeof, colocating with the constant is cleaner than re-exporting. Build passes.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/dashboard/DashboardHome.tsx`:
- Around line 28-30: The code casts session?.user?.user_metadata?.tier directly
to TierName producing userTier which may be an invalid key; validate that value
against the allowed tier keys before using it to index TIER_FUNCTION_LIMITS.
Update the logic around userTier (where it is derived from session) to check
that session?.user?.user_metadata?.tier is one of 'free'|'pro'|'enterprise' (or
exists in Object.keys(TIER_FUNCTION_LIMITS)) and fall back to 'free' if not;
ensure DirectoryPicker and any other consumers use the validated userTier so
TIER_FUNCTION_LIMITS[userTier] cannot be undefined.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/src/components/AddRepoForm.tsxfrontend/src/components/DirectoryPicker.tsxfrontend/src/components/dashboard/DashboardHome.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/DirectoryPicker.tsx
Reads tier from session.user.user_metadata.tier (set by Supabase JWT). Capitalizes first letter: 'free' -> 'Free Plan', 'pro' -> 'Pro Plan'. Falls back to 'Free Plan' when tier is not set. Previously showed 'Free Plan' for all users including Pro and Enterprise.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/dashboard/TopNav.tsx`:
- Around line 27-28: Replace the non-specific cast on the tier value with the
exported TierName type: import the type TierName from '../../config/api' and
change the cast on session?.user?.user_metadata?.tier to use TierName (e.g., set
userTier to (session?.user?.user_metadata?.tier as TierName) || 'free') so the
userTier variable and downstream tierLabel computation use the proper typed tier
instead of `as string`; ensure the import uses `import { type TierName }` for
consistency.
…ON_LIMITS Previously cast directly to TierName -- unknown values like 'beta_tester' would make TIER_FUNCTION_LIMITS[userTier] return undefined. Now checks rawTier in TIER_FUNCTION_LIMITS before cast, falls back to 'free'.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
What
Premium directory picker for monorepo subset selection. Users select which packages to index before cloning, with a tier-aware budget bar that shows estimated functions vs plan limit.
The Flow
Components
New: DirectoryPicker.tsx (320 lines)
Modified: AddRepoForm.tsx (194 lines)
Modified: DashboardHome.tsx (295 lines)
New types in types.ts: DirectoryEntry, AnalyzeResult
New: checkbox.tsx (shadcn + @radix-ui/react-checkbox)
New: TIER_FUNCTION_LIMITS in config/api.ts
Depends on
Closes OPE-110, OPE-111, OPE-112, OPE-113, OPE-114
Summary by CodeRabbit
New Features
UI Components
Visuals
UX