Skip to content

feat(workspace): collapsible file tree sidebar#1720

Merged
zhangmo8 merged 1 commit into
devfrom
workspace-panel
Jun 1, 2026
Merged

feat(workspace): collapsible file tree sidebar#1720
zhangmo8 merged 1 commit into
devfrom
workspace-panel

Conversation

@zhangmo8
Copy link
Copy Markdown
Collaborator

@zhangmo8 zhangmo8 commented Jun 1, 2026

20260601_153720.mp4

Summary by CodeRabbit

  • New Features
    • Workspace navigation sidebar now supports a collapsible layout with a resizable width adjustment via mouse drag
    • Navigation collapse/expand state is persisted across sessions
    • Added "Expand" workspace button label translations across all supported languages (16+ locales)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

WorkspacePanel 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.

Changes

Workspace Nav Collapse & Resize

Layer / File(s) Summary
Nav state management
src/renderer/src/stores/ui/sidepanel.ts
Store introduces navCollapsed and navWidth reactive persisted state with clamping constants; exposes setNavCollapsed(), toggleNavCollapsed(), and setNavWidth() actions.
Collapsible nav layout
src/renderer/src/components/sidepanel/WorkspacePanel.vue
Template refactored to use navStyle for dynamic width, collapse toggle button, and conditional rendering of labels/chevrons when not collapsed; resize handle guarded and includes data-testid; CSS added for width transitions and prefers-reduced-motion handling.
Nav resizing and expand behavior
src/renderer/src/components/sidepanel/WorkspacePanel.vue
Script imports onBeforeUnmount and WorkspaceNavSection type; implements computed navCollapsed, expandedNavWidth, and navStyle; adds handleSectionClick() to expand/toggle sections; mouse-driven resizing via startNavResize, stopNavResize, and applyPendingNavResize with requestAnimationFrame throttling and listener cleanup.
Workspace expand translations
src/renderer/src/i18n/*/chat.json
Added workspace.expand translation key across 20 language files (en-US, de-DE, fr-FR, es-ES, it-IT, ja-JP, ko-KR, zh-CN, zh-HK, zh-TW, pt-BR, ru-RU, pl-PL, da-DK, fa-IR, he-IL, id-ID, ms-MY, tr-TR, vi-VN) with locale-specific labels.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ThinkInAIXYZ/deepchat#1334: Both PRs modify the workspace sidepanel navigation components and useSidepanelStore to add collapsible behavior for Files/Git/Artifacts sections with nav width and collapse state management.

Poem

A panel that breathes and expands wide,
With mouse-drawn lines on the collapsing side,
Twenty tongues whisper "expand" with cheer—
Rails fold and unfold throughout the year! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(workspace): collapsible file tree sidebar' accurately describes the main change: adding a collapsible sidebar for the workspace file tree, matching the implementation across WorkspacePanel.vue and sidepanel store.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch workspace-panel

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 89c5b04 and b89c3ee.

📒 Files selected for processing (22)
  • src/renderer/src/components/sidepanel/WorkspacePanel.vue
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/ko-KR/chat.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/tr-TR/chat.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/renderer/src/stores/ui/sidepanel.ts

Comment on lines +9 to +18
<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"
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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).

Comment on lines +165 to +171
<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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
<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.

@zhangmo8 zhangmo8 merged commit 49fe691 into dev Jun 1, 2026
3 checks passed
@zhangmo8 zhangmo8 deleted the workspace-panel branch June 1, 2026 07:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant