Conversation
- Remove ThinkEx logo and home icon from workspace sidebar - Add ThinkEx logo to workspace header (workspace routes only) - Logo scales on hover instead of background highlight - Hide sidebar header completely on workspace routes
- Add embedded prop to Sidebar for layout-embedded positioning - When embedded, sidebar uses absolute positioning within its container - Sidebar wrapper becomes relative when embedded for proper positioning - Enables sidebar to sit below headers in flex layouts
- Add workspaceHeader prop to DashboardLayout - Move WorkspaceHeader rendering from WorkspaceSection to DashboardLayout - Header now spans full width of sidebar + workspace canvas area - Sidebar positioned below header using embedded mode - Item panels start at top (full height) instead of below header - Use nested ResizablePanelGroups: outer for chat, inner for workspace/panels - Move workspace settings/share modals to dashboard/page.tsx - Move PDF upload handler to dashboard/page.tsx - Preserve all existing functionality (chat resizing, maximized mode, etc.)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
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. 📝 WalkthroughWalkthroughWorkspace header, settings and share dialogs were moved into the dashboard; DashboardLayout was restructured to a resizable grid with a new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Header as WorkspaceHeader (UI)
participant API as /api/upload-file
participant Service as WorkspaceService
participant Store as WorkspaceState/DB
User->>Header: Select PDFs & trigger upload
Header->>API: POST multipart/form-data (files)
API-->>Header: 200 OK (uploaded file metadata/URLs)
Header->>Service: map responses -> PDF card defs, create items
Service->>Store: persist new PDF items to workspace
Store-->>Header: updated workspace state
Header-->>User: render new PDF cards
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
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. 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.
Important
Looks good to me! 👍
Reviewed everything up to ae379fc in 31 seconds. Click for details.
- Reviewed
784lines of code in6files - Skipped
0files when reviewing. - Skipped posting
0draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
Workflow ID: wflow_b2Um2NztKsR3IlKa
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
| import Link from "next/link"; | ||
| import { Button } from "@/components/ui/button"; | ||
| import { MoreVertical, LogOut, Layers, User, Mail, Play, Users, Globe, Plus, Upload, Tag, Home } from "lucide-react"; | ||
| import { MoreVertical, LogOut, Layers, User, Mail, Play, Users, Globe, Plus, Upload, Tag } from "lucide-react"; |
Greptile OverviewGreptile SummaryRestructured the dashboard layout to position the workspace header above the sidebar, creating a more unified interface. The ThinkEx logo was moved from the sidebar header to the workspace header when on workspace routes. Key Changes:
Architecture Impact: Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Page as DashboardPage
participant Layout as DashboardLayout
participant Header as WorkspaceHeader
participant Sidebar as Sidebar (embedded)
participant WorkspaceSidebar as WorkspaceSidebar
participant Section as WorkspaceSection
Page->>Layout: Pass workspaceHeader prop
Note over Page,Layout: Header now passed as prop<br/>with modal handlers
Layout->>Layout: Render header at top level
Note over Layout: Header spans full width<br/>above sidebar + workspace
Layout->>Sidebar: embedded=true
Note over Sidebar: Renders with absolute<br/>positioning (not fixed)
Sidebar->>WorkspaceSidebar: Conditionally hide header
Note over WorkspaceSidebar: Logo moved to WorkspaceHeader<br/>when on workspace route
Layout->>Section: Render workspace content
Note over Section: No longer renders header<br/>delegated to parent
Header->>Page: onOpenSettings/onOpenShare
Note over Header,Page: Modal state lifted to parent<br/>for proper scope
|
There was a problem hiding this comment.
2 issues found across 6 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="src/components/layout/DashboardLayout.tsx">
<violation number="1" location="src/components/layout/DashboardLayout.tsx:208">
P2: When two item panels are open, the workspace-area panel still uses order=1 (even though size=0) and the first item panel also gets order=1. Duplicate order values in a ResizablePanelGroup can break panel sizing/resizing. Offset item panel order when panels.length>=2 (or conditionally remove the workspace panel) so each panel has a unique order.</violation>
</file>
<file name="src/app/dashboard/page.tsx">
<violation number="1" location="src/app/dashboard/page.tsx:343">
P3: The regex used to strip the `.pdf` suffix is incorrect, so normal filenames like `report.pdf` will not have their extension removed. Use `/\.pdf$/i` to match a literal dot before `pdf`.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/app/dashboard/page.tsx`:
- Line 343: The regex used to strip the PDF extension in the object property
(the expression using file.name.replace in page.tsx) is written as /\\.pdf$/i
which matches a literal backslash + ".pdf"; change it to /\.pdf$/i (or
equivalent RegExp) so it correctly matches and removes a trailing ".pdf"
case-insensitively from file.name.
In `@src/components/workspace-canvas/WorkspaceSidebar.tsx`:
- Around line 180-203: The SidebarHeader rendering currently uses the logo Link
to "/" which causes inconsistent navigation on the /home route; use the existing
isHomeRoute variable to adjust behavior in the conditional that renders
SidebarHeader (or inside the Link) so that when isHomeRoute is true the logo
either doesn't render or its href becomes "/home" instead of "/", e.g., update
the Link href logic in WorkspaceSidebar (the block rendering SidebarHeader and
the Link component) to choose between "/" and "/home" based on isHomeRoute or
skip rendering the logo when isHomeRoute is true.
🧹 Nitpick comments (2)
src/app/dashboard/page.tsx (1)
318-365: Consider handling partial upload failures gracefully.
Promise.allwill reject entirely if any single upload fails. For a better UX when uploading multiple PDFs, consider usingPromise.allSettledto allow successful uploads to proceed even if some fail.♻️ Optional: Handle partial failures
- const uploadResults = await Promise.all(uploadPromises); + const settledResults = await Promise.allSettled(uploadPromises); + const uploadResults = settledResults + .filter((r): r is PromiseFulfilledResult<typeof uploadPromises[0] extends Promise<infer T> ? T : never> => r.status === 'fulfilled') + .map(r => r.value); + + const failedCount = settledResults.filter(r => r.status === 'rejected').length; + if (failedCount > 0 && uploadResults.length > 0) { + // Some succeeded, some failed - still create cards for successful uploads + console.warn(`${failedCount} PDF upload(s) failed`); + } else if (uploadResults.length === 0) { + throw new Error('All PDF uploads failed'); + }src/components/layout/DashboardLayout.tsx (1)
196-216: Panel key extraction could use improvement for stability.The pattern
React.isValidElement(panel) ? panel.key : \panel-${index}`` falls back to index-based keys when panel elements don't have explicit keys. This could cause unnecessary remounts if panels are reordered.Consider ensuring panels always have stable keys at the source (in
DashboardContent'spanelsmemo) rather than extracting/generating keys here. The current implementation inpage.tsxLine 278 usesitem.idas the key, which is correct, so this should work fine in practice.
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed 9712eef in 34 seconds. Click for details.
- Reviewed
183lines of code in2files - Skipped
0files when reviewing. - Skipped posting
0draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
Workflow ID: wflow_uDbftTbg30lN7Qvy
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
|
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed 9638c98 in 28 seconds. Click for details.
- Reviewed
13lines of code in1files - Skipped
0files when reviewing. - Skipped posting
0draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
Workflow ID: wflow_chA1Icld2zmIknl4
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/components/layout/DashboardLayout.tsx`:
- Around line 119-158: The workspace size callback is using the outer left-area
size so it ignores inner splits when item panels open; update the onLayout
handler (handlePanelLayout) to read the size of the inner workspace panel (id
"workspace-area-panel") instead of the outer left-area-panel and pass that value
to onWorkspaceSizeChange; specifically, locate handlePanelLayout and change its
logic to find the panel entry for "workspace-area-panel" (or derive its computed
percentage) and call onWorkspaceSizeChange with that inner-panel size so column
calculations reflect the actual workspace canvas width when item panels are
present.
🧹 Nitpick comments (1)
src/app/dashboard/page.tsx (1)
313-358: Consider partial-success handling for multi-PDF uploads.
Promise.alldrops all successful uploads if a single file fails. UsingallSettledlets you create items for successful uploads while still surfacing failures.🔧 Suggested fix (allow partial success)
- const uploadResults = await Promise.all(uploadPromises); - const pdfCardDefinitions = uploadResults.map((result) => { + const uploadResults = await Promise.allSettled(uploadPromises); + const successfulUploads = uploadResults.flatMap((r) => + r.status === "fulfilled" ? [r.value] : [] + ); + + if (successfulUploads.length === 0) { + throw new Error("Failed to upload PDFs"); + } + + const pdfCardDefinitions = successfulUploads.map((result) => { const pdfData: Partial<PdfData> = { fileUrl: result.fileUrl, filename: result.filename, fileSize: result.fileSize, }; @@ - operations.createItems(pdfCardDefinitions); + operations.createItems(pdfCardDefinitions);
| <ResizablePanelGroup | ||
| key={`layout-${effectiveChatExpanded ? "chat" : "no-chat"}`} | ||
| id={`layout-${effectiveChatExpanded ? "chat" : "no-chat"}`} | ||
| direction="horizontal" | ||
| className="flex-1 z-10" | ||
| onLayout={handlePanelLayout} | ||
| > | ||
| {/* Left Area: Split between workspace area (with header) and item panels */} | ||
| <ResizablePanel | ||
| id="left-area-panel" | ||
| order={1} | ||
| defaultSize={(() => { | ||
| if (!effectiveChatExpanded) return 100; | ||
| return 100 - PANEL_DEFAULTS.CHAT; | ||
| })()} | ||
| minSize={effectiveChatExpanded ? PANEL_DEFAULTS.WORKSPACE_MIN : 100} | ||
| > | ||
| <ResizablePanelGroup | ||
| key={`layout-${effectiveChatExpanded ? 'chat' : 'no-chat'}`} | ||
| id={`layout-${effectiveChatExpanded ? 'chat' : 'no-chat'}`} | ||
| id={`left-area-split-${panels.length}`} | ||
| // Force re-mount when panel count changes to ensure defaultSize is respected | ||
| // and to prevent "Panel data not found" errors from react-resizable-panels | ||
| key={`left-area-split-${panels.length}`} | ||
| direction="horizontal" | ||
| className="flex-1" | ||
| onLayout={handlePanelLayout} | ||
| className="flex-1 h-full" | ||
| > | ||
| {/* Workspace Panel - hidden when 2 item panels are open (split view) */} | ||
| {panels.length < 2 && ( | ||
| <ResizablePanel | ||
| id="workspace-panel" | ||
| order={1} | ||
| defaultSize={(() => { | ||
| if (panels.length === 0) { | ||
| return effectiveChatExpanded ? PANEL_DEFAULTS.WORKSPACE_WITH_CHAT : 100; | ||
| } | ||
| // 1 panel + workspace = split based on ratio (workspace gets the smaller share) | ||
| const availableSpace = effectiveChatExpanded ? (100 - PANEL_DEFAULTS.CHAT_MIN) : 100; | ||
| return availableSpace * (1 - PANEL_DEFAULTS.ITEM_PANEL_SPLIT_RATIO); | ||
| })()} | ||
| minSize={panels.length > 0 ? 20 : (effectiveChatExpanded ? PANEL_DEFAULTS.WORKSPACE_MIN : 100)} | ||
| > | ||
| <WorkspaceCanvasDropzone> | ||
| {workspaceSection} | ||
| </WorkspaceCanvasDropzone> | ||
| </ResizablePanel> | ||
| )} | ||
| {/* Workspace area: header + sidebar + workspace canvas */} | ||
| <ResizablePanel | ||
| id="workspace-area-panel" | ||
| order={0} | ||
| defaultSize={(() => { | ||
| if (panels.length === 0) { | ||
| return 100; | ||
| } | ||
| if (panels.length >= 2) { | ||
| return 0; // Hide workspace when 2 panels are open | ||
| } | ||
| return 100 * (1 - PANEL_DEFAULTS.ITEM_PANEL_SPLIT_RATIO); | ||
| })()} | ||
| minSize={panels.length >= 2 ? 0 : (panels.length > 0 ? 20 : 100)} | ||
| > |
There was a problem hiding this comment.
Workspace size tracking may drift when item panels open.
With the new inner split, onWorkspaceSizeChange still tracks the outer left-area size, so column calculations can ignore the workspace-area shrink when item panels appear. Consider updating the callback to use the inner panel’s workspace size.
🔧 Suggested fix (track inner workspace panel size)
@@
- const handlePanelLayout = useCallback((sizes: number[]) => {
- // Track workspace size for column calculations
- const workspaceSize = sizes[0];
- onWorkspaceSizeChange?.(workspaceSize);
+ const leftAreaSizeRef = useRef(100);
+
+ const handlePanelLayout = useCallback((sizes: number[]) => {
+ // Track left-area size for column calculations
+ const leftAreaSize = sizes[0];
+ leftAreaSizeRef.current = leftAreaSize;
+ // If there are no item panels, left area == workspace area
+ if (panels.length === 0) {
+ onWorkspaceSizeChange?.(leftAreaSize);
+ }
@@
- }, [onWorkspaceSizeChange]);
+ }, [onWorkspaceSizeChange, panels.length]);
+
+ const handleWorkspaceLayout = useCallback((sizes: number[]) => {
+ const workspacePctOfLeft = sizes[0];
+ const workspacePctOfTotal = (leftAreaSizeRef.current * workspacePctOfLeft) / 100;
+ onWorkspaceSizeChange?.(workspacePctOfTotal);
+ }, [onWorkspaceSizeChange]);
@@
- <ResizablePanelGroup
+ <ResizablePanelGroup
id={`left-area-split-${panels.length}`}
// Force re-mount when panel count changes to ensure defaultSize is respected
// and to prevent "Panel data not found" errors from react-resizable-panels
key={`left-area-split-${panels.length}`}
direction="horizontal"
className="flex-1 h-full"
+ onLayout={handleWorkspaceLayout}
>🤖 Prompt for AI Agents
In `@src/components/layout/DashboardLayout.tsx` around lines 119 - 158, The
workspace size callback is using the outer left-area size so it ignores inner
splits when item panels open; update the onLayout handler (handlePanelLayout) to
read the size of the inner workspace panel (id "workspace-area-panel") instead
of the outer left-area-panel and pass that value to onWorkspaceSizeChange;
specifically, locate handlePanelLayout and change its logic to find the panel
entry for "workspace-area-panel" (or derive its computed percentage) and call
onWorkspaceSizeChange with that inner-panel size so column calculations reflect
the actual workspace canvas width when item panels are present.


Important
This PR adds a workspace header above the sidebar, enabling workspace settings and sharing modals, and enhances dashboard layout with PDF uploads and resizable panels.
WorkspaceHeadercomponent topage.tsxfor workspace settings and sharing modals.DashboardContentandWorkspaceSection.DashboardLayout.sidebar.tsx.WorkspaceHeader.DashboardLayout.DashboardContent.WorkspaceSidebarto conditionally render header based on route.WorkspaceSectionto handle PDF uploads and folder operations.Sidebarcomponent to support embedded mode.This description was created by
for 9638c98. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.