feat(workspace): collapsible file tree sidebar#1720
Conversation
📝 WalkthroughWalkthroughWorkspacePanel navigation was reworked to support collapsible rail layout with dynamic width and mouse-driven resizing. The sidepanel store now persists collapse state and nav width with clamping logic. Template conditionally renders labels and chevrons based on collapse state, with a resize handle for dynamic width adjustment. Script implements resizing via requestAnimationFrame and section expansion behavior with cleanup on unmount. Workspace expand translations added across 20 languages. ChangesWorkspace Nav Collapse & Resize
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
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 docstrings
🧪 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: 2
🤖 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 `@src/renderer/src/components/sidepanel/WorkspacePanel.vue`:
- Around line 9-18: The toggle and section buttons (e.g., the button calling
sidepanelStore.toggleNavCollapsed() and the section buttons around the Icon
components) lose accessible names when navCollapsed is true because the visible
labels are removed via v-show; update these controls to preserve accessibility
by either adding appropriate aria attributes (aria-label and aria-expanded on
the main toggle, and aria-label on section buttons) or render the text as
visually-hidden content (keep in the DOM with a screen-reader-only CSS class)
instead of removing it; apply the same fix pattern to the Git and Artifacts
section buttons and any other buttons controlled by navCollapsed (references:
navCollapsed, sidepanelStore.toggleNavCollapsed, Icon).
- Around line 165-171: The resize handle should not be a keyboard-focusable
button because only mousedown is handled; replace the native <button> used for
the workspace nav resize handle (data-testid="workspace-nav-resize-handle") with
a non-interactive presentation element (e.g., a <div> or <span>) that keeps the
same classes, data-testid, and the `@mousedown`="startNavResize" listener, and
mark it non-focusable/hidden from assistive tech (role="presentation" or
aria-hidden="true" and no tabindex) so it won't receive keyboard focus while
retaining pointer-only behavior tied to the startNavResize method.
🪄 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: 8acd70bc-ac8f-4e76-a21a-c155783da9ae
📒 Files selected for processing (22)
src/renderer/src/components/sidepanel/WorkspacePanel.vuesrc/renderer/src/i18n/da-DK/chat.jsonsrc/renderer/src/i18n/de-DE/chat.jsonsrc/renderer/src/i18n/en-US/chat.jsonsrc/renderer/src/i18n/es-ES/chat.jsonsrc/renderer/src/i18n/fa-IR/chat.jsonsrc/renderer/src/i18n/fr-FR/chat.jsonsrc/renderer/src/i18n/he-IL/chat.jsonsrc/renderer/src/i18n/id-ID/chat.jsonsrc/renderer/src/i18n/it-IT/chat.jsonsrc/renderer/src/i18n/ja-JP/chat.jsonsrc/renderer/src/i18n/ko-KR/chat.jsonsrc/renderer/src/i18n/ms-MY/chat.jsonsrc/renderer/src/i18n/pl-PL/chat.jsonsrc/renderer/src/i18n/pt-BR/chat.jsonsrc/renderer/src/i18n/ru-RU/chat.jsonsrc/renderer/src/i18n/tr-TR/chat.jsonsrc/renderer/src/i18n/vi-VN/chat.jsonsrc/renderer/src/i18n/zh-CN/chat.jsonsrc/renderer/src/i18n/zh-HK/chat.jsonsrc/renderer/src/i18n/zh-TW/chat.jsonsrc/renderer/src/stores/ui/sidepanel.ts
| <button | ||
| class="flex w-full shrink-0 items-center gap-2 px-3 py-2 text-muted-foreground transition-colors hover:text-foreground" | ||
| type="button" | ||
| :title="navCollapsed ? t('chat.workspace.expand') : t('chat.workspace.collapse')" | ||
| @click="sidepanelStore.toggleNavCollapsed()" | ||
| > | ||
| <Icon | ||
| :icon="navCollapsed ? 'lucide:panel-left-open' : 'lucide:panel-left-close'" | ||
| class="h-3.5 w-3.5 shrink-0" | ||
| /> |
There was a problem hiding this comment.
Collapsed rail buttons lose their accessible names.
When navCollapsed is true, these controls become icon-only and the v-show labels drop out of the accessibility tree. That leaves the main toggle and section buttons unlabeled for screen-reader users. Add an aria-label/aria-expanded, or keep the text as visually hidden content instead of removing it.
Suggested fix pattern
<button
class="flex w-full shrink-0 items-center gap-2 px-3 py-2 text-muted-foreground transition-colors hover:text-foreground"
type="button"
+ :aria-label="navCollapsed ? t('chat.workspace.expand') : t('chat.workspace.collapse')"
+ :aria-expanded="!navCollapsed"
:title="navCollapsed ? t('chat.workspace.expand') : t('chat.workspace.collapse')"
`@click`="sidepanelStore.toggleNavCollapsed()"
>
@@
<button
class="flex w-full items-center gap-2 px-3 py-2 text-left text-xs font-medium"
type="button"
+ :aria-label="t('chat.workspace.sections.files')"
+ :aria-expanded="sessionState.sections.files"
`@click`="handleSectionClick('files')"
>
<Icon icon="lucide:folder-tree" class="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
- <span v-show="!navCollapsed" class="flex-1 truncate">{{
+ <span :class="navCollapsed ? 'sr-only' : 'flex-1 truncate'">{{
t('chat.workspace.sections.files')
}}</span>Apply the same pattern to the Git and Artifacts section buttons.
Also applies to: 22-35, 77-94, 123-142
🤖 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 `@src/renderer/src/components/sidepanel/WorkspacePanel.vue` around lines 9 -
18, The toggle and section buttons (e.g., the button calling
sidepanelStore.toggleNavCollapsed() and the section buttons around the Icon
components) lose accessible names when navCollapsed is true because the visible
labels are removed via v-show; update these controls to preserve accessibility
by either adding appropriate aria attributes (aria-label and aria-expanded on
the main toggle, and aria-label on section buttons) or render the text as
visually-hidden content (keep in the DOM with a screen-reader-only CSS class)
instead of removing it; apply the same fix pattern to the Git and Artifacts
section buttons and any other buttons controlled by navCollapsed (references:
navCollapsed, sidepanelStore.toggleNavCollapsed, Icon).
| <button | ||
| v-if="!navCollapsed" | ||
| data-testid="workspace-nav-resize-handle" | ||
| class="absolute inset-y-0 right-0 z-10 w-1.5 cursor-col-resize" | ||
| type="button" | ||
| @mousedown="startNavResize" | ||
| ></button> |
There was a problem hiding this comment.
Don't expose the mouse-only resize handle as a button.
This is keyboard-focusable, but it only handles mousedown, so users can tab to an unlabeled control that has no keyboard behavior. If resize is intentionally pointer-only here, render a non-focusable presentation element instead of a native button.
Suggested fix
- <button
+ <div
v-if="!navCollapsed"
data-testid="workspace-nav-resize-handle"
class="absolute inset-y-0 right-0 z-10 w-1.5 cursor-col-resize"
- type="button"
+ aria-hidden="true"
`@mousedown`="startNavResize"
- ></button>
+ ></div>📝 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.
| <button | |
| v-if="!navCollapsed" | |
| data-testid="workspace-nav-resize-handle" | |
| class="absolute inset-y-0 right-0 z-10 w-1.5 cursor-col-resize" | |
| type="button" | |
| @mousedown="startNavResize" | |
| ></button> | |
| <div | |
| v-if="!navCollapsed" | |
| data-testid="workspace-nav-resize-handle" | |
| class="absolute inset-y-0 right-0 z-10 w-1.5 cursor-col-resize" | |
| aria-hidden="true" | |
| `@mousedown`="startNavResize" | |
| ></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 `@src/renderer/src/components/sidepanel/WorkspacePanel.vue` around lines 165 -
171, The resize handle should not be a keyboard-focusable button because only
mousedown is handled; replace the native <button> used for the workspace nav
resize handle (data-testid="workspace-nav-resize-handle") with a non-interactive
presentation element (e.g., a <div> or <span>) that keeps the same classes,
data-testid, and the `@mousedown`="startNavResize" listener, and mark it
non-focusable/hidden from assistive tech (role="presentation" or
aria-hidden="true" and no tabindex) so it won't receive keyboard focus while
retaining pointer-only behavior tied to the startNavResize method.
20260601_153720.mp4
Summary by CodeRabbit