feat(dashboard): scrollable, autocomplete, bookmarkable project selector#1467
Conversation
…th exponential backoff Add domain-specific error classes (engine-errors.ts) replacing generic catch blocks with typed, classifiable errors: - EngineError base with code/retryable/details fields - TransientError hierarchy: NetworkError, ServiceUnavailableError, TimeoutError - PermanentError hierarchy: ConfigurationError, ValidationError - RateLimitError (triggers global pause, not local retry) - classifyThrownError() bridge from legacy string-based detection Add general-purpose retry with exponential backoff (retry-with-backoff.ts): - withRetry() wraps async ops with configurable retries for transient errors - Exponential backoff: delay = min(baseMs * 2^attempt, maxMs) - Three jitter strategies: full (default), equal, none - Per-attempt timeout support via timeoutMs option - AbortSignal integration for task pause/cancel/shutdown - Custom isRetryable check for domain-specific retry logic - withRetryResult() variant returning retry metadata - Never retries rate-limit errors (delegates to global pause) 53 new tests covering error hierarchy, backoff math, cancellable sleep, retry on transient errors, non-retryable fail-fast, rate-limit bypass, abort cancellation, per-attempt timeout, custom predicates.
- Fix: ProjectSelector dropdown now scrolls (max-height + overflow-y) when project list exceeds visible area, with scrollIntoView on keyboard navigation - Feat: Always-visible search input with type-ahead filtering, HighlightMatch text highlighting, exact match detection + auto-select on Enter - Feat: Project bookmarking via localStorage (useProjectBookmarks hook), star toggle on each item, bookmarked section shown at top of dropdown - Refactor: Header.tsx now imports standalone ProjectSelector instead of using an inline copy that lacked these features - Tests: 131 tests passing across ProjectSelector + useProjectBookmarks
… match - Make onSelect optional in ProjectSelectorProps to match Header usage where onSelectProject may be undefined (fixes runtime crash) - Exclude currentProject from exact match detection so typing the current project name doesn't show misleading exact-match banner - Addresses Gemini Code Assist review findings
…ements feat(dashboard): scrollable, autocomplete, bookmarkable project selector
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughAdds bookmarking and exact-match autocomplete to the dashboard ProjectSelector (now a standalone component) and introduces a typed EngineError hierarchy plus exponential-backoff retry utilities with cancellable delays and per-attempt timeouts. ChangesDashboard Project Selector Enhancement
Engine Retry Utilities with Error Hierarchy
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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 |
Greptile SummaryThis PR enhances the dashboard project selector with scrollable dropdown, autocomplete/type-ahead with
Confidence Score: 5/5Safe to merge — all previously flagged regressions are addressed and no new blocking issues introduced. The three dashboard defects and two engine defects from prior review rounds are all corrected. The one remaining finding is a minor cleanup pattern in cancellableSleep where the resolve reassignment is dead code — cosmetic and no correctness impact under normal usage. No files require special attention. Important Files Changed
Reviews (4): Last reviewed commit: "fix: address PR review feedback (#1467)" | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/dashboard/app/components/ProjectSelector.tsx (1)
297-305:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAdd optional chaining for
onSelectcall.Same issue as the keyboard handler:
onSelectis optional but called directly at line 300. Ifundefined, clicking a project will crash.🛡️ Proposed fix
const handleSelectProject = useCallback( (project: ProjectInfo) => { - onSelect(project); + onSelect?.(project); setIsOpen(false); setSearchQuery(""); }, [onSelect] );🤖 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 `@packages/dashboard/app/components/ProjectSelector.tsx` around lines 297 - 305, handleSelectProject currently calls the potentially-undefined callback onSelect directly, which can crash; change the invocation to use optional chaining (call onSelect?.(project)) so it only executes when provided, leaving the rest of the logic (setIsOpen(false), setSearchQuery("")) intact; update the useCallback body for handleSelectProject to use onSelect?.(project) and keep the dependency array as [onSelect].
🤖 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 `@packages/dashboard/app/components/Header.tsx`:
- Around line 839-846: The desktop/tablet rendering of StandaloneProjectSelector
should also guard that onSelectProject is provided; update the conditional that
currently checks {!isMobile && projects.length >= 1 && onViewAllProjects} to
additionally require onSelectProject, so StandaloneProjectSelector is only
rendered when onSelectProject is defined; ensure the props passed (projects,
currentProject, onViewAll, onSelect) remain unchanged but that onSelect receives
a defined function to avoid crashes in StandaloneProjectSelector /
ProjectSelector.
In `@packages/dashboard/app/components/ProjectSelector.tsx`:
- Around line 222-250: In ProjectSelector.tsx update the Enter key handling so
the optional onSelect prop is invoked with optional chaining for highlighted
items: replace direct calls to onSelect(displayProjects.bookmarked[...]),
onSelect(displayProjects.recent[...]) and onSelect(displayProjects.others[...])
with onSelect?.(...) respectively; leave the View All path (onViewAll()),
setIsOpen(false) and setSearchQuery("") behavior unchanged so Enter no longer
throws when onSelect is undefined.
In `@packages/engine/src/engine-errors.ts`:
- Line 247: The regex in the conditional is using unnecessary backslash-escaped
quotes which triggers the no-useless-escape lint rule; update the regex in the
if condition that tests message (the /.../i.test(message) expression) to remove
the redundant backslashes and use a plain alternation like
"type":"server_error"|"code":"server_error" (keep the /.../i literal and
.test(message) unchanged) so the pattern reads correctly without escaped quotes.
In `@packages/engine/src/retry-with-backoff.ts`:
- Around line 43-45: Short-circuit typed RateLimitError before running custom
retry checks by detecting the typed error with classifyThrownError or the
EngineError/RateLimitError type before inspecting message text: in the retry
logic where you call the isRetryable callback (and similarly in the block around
where isUsageLimitError is used), first call classifyThrownError(thrown) (or
check thrown instanceof RateLimitError if available) and immediately treat
classified "RateLimitError" as non-retryable, returning/throwing early so a
broad isRetryable implementation cannot override the global rate-limit pause.
- Around line 246-248: Remove the unused retry bookkeeping variables to satisfy
the linter: delete the declarations of startTime and retryCount in
retry-with-backoff.ts (the const startTime = Date.now(); and let retryCount = 0;
lines) and remove any other stray retryCount/startTime declarations (e.g., the
occurrence around line 301). Keep lastError (let lastError: EngineError |
undefined;) if it is referenced elsewhere; otherwise remove it as well. Ensure
no remaining references to these removed symbols remain in functions such as the
retryWithBackoff logic so TypeScript no-unused-vars errors are resolved.
- Around line 170-210: withTimeout currently aborts an internal AbortController
but never passes its signal to the attempt function, so timed-out attempts keep
running; change withTimeout<T>(fn: () => Promise<T>, ...) to withTimeout<T>(fn:
(signal?: AbortSignal) => Promise<T>, ...) and invoke fn(ac.signal) so the
attempt can cooperatively abort; keep the existing parentSignal linking and
timer logic but ensure ac.abort(...) is called on parent abort or timeout (as it
already is) and that you update all call sites (including the withRetry usage
around lines 231-260) to accept/pass an AbortSignal into their attempt
functions.
---
Outside diff comments:
In `@packages/dashboard/app/components/ProjectSelector.tsx`:
- Around line 297-305: handleSelectProject currently calls the
potentially-undefined callback onSelect directly, which can crash; change the
invocation to use optional chaining (call onSelect?.(project)) so it only
executes when provided, leaving the rest of the logic (setIsOpen(false),
setSearchQuery("")) intact; update the useCallback body for handleSelectProject
to use onSelect?.(project) and keep the dependency array as [onSelect].
🪄 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 Plus
Run ID: 418cc0d4-95ce-438c-afd1-ee9113299f9a
📒 Files selected for processing (9)
packages/dashboard/app/components/Header.tsxpackages/dashboard/app/components/ProjectSelector.csspackages/dashboard/app/components/ProjectSelector.tsxpackages/dashboard/app/components/__tests__/ProjectSelector.test.tsxpackages/dashboard/app/hooks/__tests__/useProjectBookmarks.test.tspackages/dashboard/app/hooks/useProjectBookmarks.tspackages/engine/src/__tests__/retry-with-backoff.test.tspackages/engine/src/engine-errors.tspackages/engine/src/retry-with-backoff.ts
Added flex: 1 to .agents-view so it expands to fill the project-content parent instead of shrinking to content width.
|
Thanks! Please address comments and will get it merged in |
Same pattern as agents-view fix — .chat-view had display:flex + height:100% but no flex or width, so it shrank to content width (~270px) instead of filling the .project-content parent.
- guard optional project selection callbacks and keep bookmarked search matches visible - use structured timeout/rate-limit handling in retry backoff - add focused regressions for selector and retry behavior Note: pnpm test passed the merge gate but hit pre-existing non-blocking engine mock failures in changed-package tests.
Addressed in a27921a: click and keyboard selection now guard optional |
Summary
Enhances the Fusion dashboard project selector dropdown with 3 features:
1. Scrollable dropdown
max-height: min(480px, calc(100vh - 120px))withoverflow-y: autoscrollIntoView({ block: "nearest" })on keyboard navigation2. Autocomplete / type-ahead
HighlightMatchcomponent renders matched substrings with<mark>(bold + accent underline)3. Project bookmarking (localStorage)
useProjectBookmarkshook:fusion_project_bookmarkskey, JSON array of IDsStar) on each item — opacity-on-hover pattern, filled when bookmarkedstopPropagationon star click — toggling does not trigger selection or close dropdownRefactor
ProjectSelector(~130 lines) and now imports the standalone component from./ProjectSelectorwith all 3 enhancementsFiles changed
ProjectSelector.tsxProjectSelector.cssHeader.tsxProjectSelector.test.tsxuseProjectBookmarks.tsuseProjectBookmarks.test.tsTest plan
ProjectSelector.test.tsx+useProjectBookmarks.test.ts)tsc --noEmitclean — zero errorspnpm build:clientsucceedsSummary by CodeRabbit
New Features
Bug Fixes / Reliability
Tests