Conversation
|
Caution Review failedPull request was closed or merged during review Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces flat sidebar with collapsible pinned/group sections, implements animated (or reduced-motion) pin/unpin "pin flight" via DOM-clone and timed feedback, updates session-item props/emits/UI for pin/delete/hero/pin-feedback, removes rename/clear dialogs, updates tests and adds i18n Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Item as WindowSideBarSessionItem
participant Sidebar as WindowSideBar
participant DOM
participant RAF
User->>Item: click pin button
Item->>Sidebar: emit toggle-pin(session)
Sidebar->>Sidebar: check prefers-reduced-motion
alt reduced motion
Sidebar->>Sidebar: commit pin toggle (no animation)
Sidebar->>Item: update region / pinFeedbackMode
else animated flow
Sidebar->>DOM: clone source session element (fixed pos)
Sidebar->>Sidebar: set pinFlightSessionId
Sidebar->>Sidebar: commit pin state (mid-flight)
Sidebar->>Sidebar: preserve/restore scrollTop
Sidebar->>RAF: schedule animation frames
RAF->>DOM: animate clone to destination
RAF-->>Sidebar: animation complete
Sidebar->>Item: set timed pinFeedbackMode
Sidebar->>Sidebar: clear feedback timer
end
Sidebar->>Item: re-render with new data-* attributes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/src/components/WindowSideBar.vue`:
- Around line 323-325: The click handler reads session.isPinned into nextPinned
and races because pinFlightSessionId / pinFeedbackSessionId / pinFeedbackMode
are global single-slot flags; change the handler(s) that compute nextPinned (the
toggle/pin toggle functions around the usages of pinFlightSessionId,
pinFeedbackSessionId, pinFeedbackMode and nextPinned) to serialize toggles:
either (A) early-return if pinFlightSessionId or pinFeedbackSessionId is set for
that session, or (B) replace the single-slot refs with a per-session pending map
(e.g., pendingPinBySessionId) keyed by session.id so each session tracks its own
pending state; ensure the async store.update call is awaited before clearing the
pending flag and that the UI reads pending state per-session to prevent stale
nextPinned reads.
In `@src/renderer/src/components/WindowSideBarSessionItem.vue`:
- Around line 23-29: The component declares emits for 'rename' and 'clear' via
defineEmits in WindowSideBarSessionItem.vue but the template no longer renders
UI controls to trigger them, while WindowSideBar.vue still listens for those
events; either remove the unused emits/listener usage or re-add the UI actions
(buttons/menu items) that call emit('rename', session) and emit('clear',
session). Locate defineEmits in WindowSideBarSessionItem.vue and either delete
the 'rename' and 'clear' entries (and corresponding listeners in
WindowSideBar.vue) or restore the template action elements and their click
handlers so they call emit('rename', session) and emit('clear', session) where
session is the current UISession.
- Around line 40-48: The session row is a non-focusable div so keyboard users
cannot activate it; make it keyboard-accessible by either converting the div
into a semantic button or adding tabindex="0" and role="button" and a key
handler that triggers the same action as click. Specifically, keep the existing
attributes and class bindings on the element (the one with
`@click`="emit('select', session)"), add tabindex="0" and role="button" if you
don't convert to a <button>, and add a `@keydown` handler that listens for "Enter"
and " " (Space) and calls emit('select', session) (preventDefault for Space to
avoid scrolling). Ensure the handler uses the same emit('select', session)
invocation so keyboard and mouse activation behave identically.
- Around line 100-297: The scoped CSS introduces base/variant layout and colors
that should be moved into Tailwind utilities: remove/replace rules for
.session-item, .pin-button, .right-button, .session-title,
.session-title__label/.session-title__sheen and pin-button modifiers
(.pin-button--idle, .pin-button--active) with equivalent Tailwind classes in the
component template, keeping only the custom animations, keyframes, and complex
masks/gradients (e.g., ::after, ::before, data-pin-fx animations,
session-loading-sheen) in the <style> block; update class bindings to use
Tailwind utility names and preserve data-attribute selectors (data-pin-fx,
data-hero-hidden) to trigger the remaining animation CSS.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8f2cd1cb-986d-4726-9bcb-765352540acf
📒 Files selected for processing (4)
src/renderer/src/components/WindowSideBar.vuesrc/renderer/src/components/WindowSideBarSessionItem.vuetest/renderer/components/WindowSideBar.test.tstest/renderer/components/WindowSideBarSessionItem.test.ts
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/renderer/src/components/WindowSideBar.vue (1)
335-338:⚠️ Potential issue | 🟠 MajorStill serialize pin flights before starting another toggle.
These refs are still single-slot, but
handleTogglePin()can start a second flight immediately. Pinning another session while one animation is in progress will overwrite the first session'sheroHidden/ feedback state and can leave the visual result desynced. Either gate new toggles until the current flight settles or track pending state per session.Also applies to: 582-650, 655-665
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/src/components/WindowSideBar.vue` around lines 335 - 338, handleTogglePin can start a new "flight" while pins are single-slot refs (pinFlightSessionId, pinFeedbackSessionId, pinFeedbackMode), causing concurrent toggles to overwrite heroHidden/feedback state; fix by serializing flights or making state per-session: either add a gate boolean (e.g., isPinFlightInProgress) and return early from handleTogglePin until the current flight settles (clear the gate when the animation/timeout or transitionend completes), or replace the single-slot refs with a Map keyed by session id (e.g., pinFlightState: Map<string, { heroHidden:boolean, feedbackMode:PinFeedbackMode, pending:boolean }>) and update handleTogglePin to set/clear the session's pending flag so simultaneous toggles target different sessions without clobbering hero/feedback state; update any code reading pinFlightSessionId/pinFeedbackSessionId/pinFeedbackMode to use the chosen mechanism.src/renderer/src/components/WindowSideBarSessionItem.vue (1)
38-58:⚠️ Potential issue | 🟠 MajorKeyboard-only users still can't reach the row's primary actions.
Lines 39-45 keep session selection on a non-focusable
div, and Lines 179-193 hide the idle pin button withvisibility: hidden, which removes it from the tab order. On an unpinned session, a keyboard user currently can't open the thread or pin it. Please add a keyboard activation path for selection and keep the idle pin control focusable.Also applies to: 179-193
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/src/components/WindowSideBarSessionItem.vue` around lines 38 - 58, The session row is not keyboard-accessible: make the root session container (the div with class "session-item" that currently has `@click`="emit('select', session)") keyboard-focusable by adding tabindex="0" and bind key handlers (Enter/Space) to call the same emit('select', session) action so keyboard users can activate selection; for the pin control (button with class "pin-button" and :class based on session.isPinned) ensure the idle state is not removed from the tab order—stop using visibility:hidden to hide it and instead keep the button focusable (remove aria-hidden/visibility toggles or ensure tabindex is present when idle) so `@click.stop`="emit('toggle-pin', session)" remains reachable via keyboard and update any associated CSS that hides the idle pin to a visual-only technique that preserves focusability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/src/components/WindowSideBar.vue`:
- Around line 721-742: The CSS transition uses a hard-coded max-height (720px)
in the .sidebar-group-collapse-enter-to / .sidebar-group-collapse-leave-from
rules which causes clipping for taller groups; replace the fixed cap by
computing and applying the actual element.scrollHeight as an inline max-height
during the transition (use Vue transition hooks like
beforeEnter/enter/afterLeave in WindowSideBar.vue) or alternatively animate only
transform/opacity if you prefer no height animation; update the transition hooks
to set el.style.maxHeight = el.scrollHeight + 'px' on enter and
el.style.maxHeight = '0px' on leave and clear the style after the animation
completes so the collapse/expand is smooth for any group height.
- Around line 206-213: The folder icon binding is incorrectly using
isPinnedSectionCollapsed instead of the group's collapsed state; update the Icon
component that currently uses :icon="isPinnedSectionCollapsed ?
'lucide:folder-closed' : 'lucide:folder-open'" to derive its value from
isGroupCollapsed(group) (the same predicate used for the chevron) so each
group's folder-open/folder-closed icon stays in sync with
isGroupCollapsed(group).
---
Duplicate comments:
In `@src/renderer/src/components/WindowSideBar.vue`:
- Around line 335-338: handleTogglePin can start a new "flight" while pins are
single-slot refs (pinFlightSessionId, pinFeedbackSessionId, pinFeedbackMode),
causing concurrent toggles to overwrite heroHidden/feedback state; fix by
serializing flights or making state per-session: either add a gate boolean
(e.g., isPinFlightInProgress) and return early from handleTogglePin until the
current flight settles (clear the gate when the animation/timeout or
transitionend completes), or replace the single-slot refs with a Map keyed by
session id (e.g., pinFlightState: Map<string, { heroHidden:boolean,
feedbackMode:PinFeedbackMode, pending:boolean }>) and update handleTogglePin to
set/clear the session's pending flag so simultaneous toggles target different
sessions without clobbering hero/feedback state; update any code reading
pinFlightSessionId/pinFeedbackSessionId/pinFeedbackMode to use the chosen
mechanism.
In `@src/renderer/src/components/WindowSideBarSessionItem.vue`:
- Around line 38-58: The session row is not keyboard-accessible: make the root
session container (the div with class "session-item" that currently has
`@click`="emit('select', session)") keyboard-focusable by adding tabindex="0" and
bind key handlers (Enter/Space) to call the same emit('select', session) action
so keyboard users can activate selection; for the pin control (button with class
"pin-button" and :class based on session.isPinned) ensure the idle state is not
removed from the tab order—stop using visibility:hidden to hide it and instead
keep the button focusable (remove aria-hidden/visibility toggles or ensure
tabindex is present when idle) so `@click.stop`="emit('toggle-pin', session)"
remains reachable via keyboard and update any associated CSS that hides the idle
pin to a visual-only technique that preserves focusability.
🪄 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: 7ae6b0a4-ac72-485f-90bc-9ab2cb530656
📒 Files selected for processing (15)
src/renderer/src/components/WindowSideBar.vuesrc/renderer/src/components/WindowSideBarSessionItem.vuesrc/renderer/src/i18n/da-DK/chat.jsonsrc/renderer/src/i18n/en-US/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/ja-JP/chat.jsonsrc/renderer/src/i18n/ko-KR/chat.jsonsrc/renderer/src/i18n/pt-BR/chat.jsonsrc/renderer/src/i18n/ru-RU/chat.jsonsrc/renderer/src/i18n/zh-CN/chat.jsonsrc/renderer/src/i18n/zh-HK/chat.jsonsrc/renderer/src/i18n/zh-TW/chat.jsontest/renderer/components/WindowSideBar.test.ts
✅ Files skipped from review due to trivial changes (12)
- src/renderer/src/i18n/da-DK/chat.json
- src/renderer/src/i18n/zh-CN/chat.json
- src/renderer/src/i18n/en-US/chat.json
- src/renderer/src/i18n/he-IL/chat.json
- src/renderer/src/i18n/fa-IR/chat.json
- src/renderer/src/i18n/zh-HK/chat.json
- src/renderer/src/i18n/fr-FR/chat.json
- src/renderer/src/i18n/pt-BR/chat.json
- src/renderer/src/i18n/zh-TW/chat.json
- src/renderer/src/i18n/ja-JP/chat.json
- src/renderer/src/i18n/ko-KR/chat.json
- src/renderer/src/i18n/ru-RU/chat.json
🚧 Files skipped from review as they are similar to previous changes (1)
- test/renderer/components/WindowSideBar.test.ts
| <Icon | ||
| :icon="isGroupCollapsed(group) ? 'lucide:chevron-right' : 'lucide:chevron-down'" | ||
| class="h-3 w-3 shrink-0" | ||
| /> | ||
| <span class="shrink-0 size-6 flex items-center justify-center"> | ||
| <Icon | ||
| :icon="isPinnedSectionCollapsed ? 'lucide:folder-closed' : 'lucide:folder-open'" | ||
| class="size-4" |
There was a problem hiding this comment.
Drive the folder icon from isGroupCollapsed(group).
Lines 206-213 already use isGroupCollapsed(group) for the chevron, but Lines 212-213 still key the folder icon off isPinnedSectionCollapsed. That leaves every non-pinned group's folder-open/folder-closed indicator out of sync.
🩹 Proposed fix
- :icon="isPinnedSectionCollapsed ? 'lucide:folder-closed' : 'lucide:folder-open'"
+ :icon="isGroupCollapsed(group) ? 'lucide:folder-closed' : 'lucide:folder-open'"📝 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.
| <Icon | |
| :icon="isGroupCollapsed(group) ? 'lucide:chevron-right' : 'lucide:chevron-down'" | |
| class="h-3 w-3 shrink-0" | |
| /> | |
| <span class="shrink-0 size-6 flex items-center justify-center"> | |
| <Icon | |
| :icon="isPinnedSectionCollapsed ? 'lucide:folder-closed' : 'lucide:folder-open'" | |
| class="size-4" | |
| <Icon | |
| :icon="isGroupCollapsed(group) ? 'lucide:chevron-right' : 'lucide:chevron-down'" | |
| class="h-3 w-3 shrink-0" | |
| /> | |
| <span class="shrink-0 size-6 flex items-center justify-center"> | |
| <Icon | |
| :icon="isGroupCollapsed(group) ? 'lucide:folder-closed' : 'lucide:folder-open'" | |
| class="size-4" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/src/components/WindowSideBar.vue` around lines 206 - 213, The
folder icon binding is incorrectly using isPinnedSectionCollapsed instead of the
group's collapsed state; update the Icon component that currently uses
:icon="isPinnedSectionCollapsed ? 'lucide:folder-closed' : 'lucide:folder-open'"
to derive its value from isGroupCollapsed(group) (the same predicate used for
the chevron) so each group's folder-open/folder-closed icon stays in sync with
isGroupCollapsed(group).
| .sidebar-group-collapse-enter-active, | ||
| .sidebar-group-collapse-leave-active { | ||
| overflow: hidden; | ||
| transition: | ||
| max-height 180ms ease, | ||
| opacity 160ms ease, | ||
| transform 180ms ease; | ||
| } | ||
|
|
||
| .sidebar-group-collapse-enter-from, | ||
| .sidebar-group-collapse-leave-to { | ||
| max-height: 0; | ||
| opacity: 0; | ||
| transform: translateY(-4px); | ||
| } | ||
|
|
||
| .sidebar-group-collapse-enter-to, | ||
| .sidebar-group-collapse-leave-from { | ||
| max-height: 720px; | ||
| opacity: 1; | ||
| transform: translateY(0); | ||
| } |
There was a problem hiding this comment.
The collapse transition will snap for taller groups.
Lines 737-740 cap the animation at max-height: 720px. Once a group grows past that, expand/collapse will clip and jump instead of transitioning cleanly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/src/components/WindowSideBar.vue` around lines 721 - 742, The
CSS transition uses a hard-coded max-height (720px) in the
.sidebar-group-collapse-enter-to / .sidebar-group-collapse-leave-from rules
which causes clipping for taller groups; replace the fixed cap by computing and
applying the actual element.scrollHeight as an inline max-height during the
transition (use Vue transition hooks like beforeEnter/enter/afterLeave in
WindowSideBar.vue) or alternatively animate only transform/opacity if you prefer
no height animation; update the transition hooks to set el.style.maxHeight =
el.scrollHeight + 'px' on enter and el.style.maxHeight = '0px' on leave and
clear the style after the animation completes so the collapse/expand is smooth
for any group height.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/renderer/src/components/WindowSideBar.vue (1)
1-1:⚠️ Potential issue | 🟡 MinorRun Prettier to fix formatting issues.
The CI pipeline reports Prettier check failures. As per coding guidelines, run
pnpm run formatto fix code style issues before merging.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/src/components/WindowSideBar.vue` at line 1, The file WindowSideBar.vue has Prettier formatting failures; run the formatter (pnpm run format) or directly run Prettier on the WindowSideBar.vue component to fix spacing/indentation in the <template> block and any other sections, then stage the formatted changes and push so CI passes.
♻️ Duplicate comments (3)
src/renderer/src/components/WindowSideBar.vue (3)
212-216:⚠️ Potential issue | 🟡 MinorFolder icon uses wrong collapsed state.
The folder icon is bound to
isPinnedSectionCollapsedbut should useisGroupCollapsed(group)to stay in sync with the chevron and actual group state.🐛 Proposed fix
<span class="shrink-0 size-6 flex items-center justify-center"> <Icon - :icon="isPinnedSectionCollapsed ? 'lucide:folder-closed' : 'lucide:folder-open'" + :icon="isGroupCollapsed(group) ? 'lucide:folder-closed' : 'lucide:folder-open'" class="size-4" /> </span>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/src/components/WindowSideBar.vue` around lines 212 - 216, The folder Icon currently checks isPinnedSectionCollapsed but should reflect each group's real collapsed state; replace the binding to use isGroupCollapsed(group) so the icon stays synchronized with the chevron and group collapse state (update the Icon :icon prop where isPinnedSectionCollapsed is referenced inside the WindowSideBar component to call isGroupCollapsed(group) instead).
722-735:⚠️ Potential issue | 🟠 MajorPin toggle lacks serialization guard.
Rapid clicks can race because
nextPinnedis derived fromsession.isPinnedbefore the async store update completes, and the single-slot state refs (pinFlightSessionId,pinFeedbackSessionId) cannot track multiple concurrent toggles.🛡️ Proposed fix: early-return if animation is in progress
const handleTogglePin = async (session: UISession) => { + // Prevent concurrent toggles while animation/feedback is in progress + if (pinFlightSessionId.value === session.id || pinFeedbackSessionId.value === session.id) { + return + } + const nextPinned = !session.isPinned try {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/src/components/WindowSideBar.vue` around lines 722 - 735, The pin toggle races when users click rapidly because handleTogglePin computes nextPinned from session.isPinned and launches async work without a serialization guard; fix by checking and setting the single-slot guard (pinFlightSessionId) at the start of handleTogglePin for the given session id (early-return if another flight is active for that session), compute nextPinned after acquiring the guard (or read the latest store/session state right before calling commitPinToggle/animatePinFlight), then clear the guard in a finally block; ensure you also avoid stomping pinFeedbackSessionId during an in-flight toggle and reference the existing functions/refs: handleTogglePin, commitPinToggle, animatePinFlight, pinFlightSessionId, and pinFeedbackSessionId.
804-809:⚠️ Potential issue | 🟡 MinorHard-coded max-height may cause transition jump for tall groups.
The
max-height: 720pxcap will cause the expand/collapse transition to clip and snap for groups containing more than ~18-20 sessions. Consider using Vue transition hooks to dynamically setmax-heightto the element'sscrollHeight.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/src/components/WindowSideBar.vue` around lines 804 - 809, The CSS rule using .sidebar-group-collapse-enter-to and .sidebar-group-collapse-leave-from with a hard-coded max-height: 720px causes clipping for tall groups; in WindowSideBar.vue replace this static cap by removing the fixed max-height and implement Vue transition hooks (e.g. beforeEnter/enter/afterEnter and beforeLeave/leave/afterLeave) on the transition wrapping the group: in beforeEnter set el.style.maxHeight = '0px', in enter set el.style.maxHeight = el.scrollHeight + 'px' and clear it in afterEnter, and for collapse set el.style.maxHeight = el.scrollHeight + 'px' in beforeLeave then to '0px' in leave and clear in afterLeave so the expand/collapse animates to the element's actual height instead of 720px.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/renderer/src/components/WindowSideBar.vue`:
- Line 1: The file WindowSideBar.vue has Prettier formatting failures; run the
formatter (pnpm run format) or directly run Prettier on the WindowSideBar.vue
component to fix spacing/indentation in the <template> block and any other
sections, then stage the formatted changes and push so CI passes.
---
Duplicate comments:
In `@src/renderer/src/components/WindowSideBar.vue`:
- Around line 212-216: The folder Icon currently checks isPinnedSectionCollapsed
but should reflect each group's real collapsed state; replace the binding to use
isGroupCollapsed(group) so the icon stays synchronized with the chevron and
group collapse state (update the Icon :icon prop where isPinnedSectionCollapsed
is referenced inside the WindowSideBar component to call isGroupCollapsed(group)
instead).
- Around line 722-735: The pin toggle races when users click rapidly because
handleTogglePin computes nextPinned from session.isPinned and launches async
work without a serialization guard; fix by checking and setting the single-slot
guard (pinFlightSessionId) at the start of handleTogglePin for the given session
id (early-return if another flight is active for that session), compute
nextPinned after acquiring the guard (or read the latest store/session state
right before calling commitPinToggle/animatePinFlight), then clear the guard in
a finally block; ensure you also avoid stomping pinFeedbackSessionId during an
in-flight toggle and reference the existing functions/refs: handleTogglePin,
commitPinToggle, animatePinFlight, pinFlightSessionId, and pinFeedbackSessionId.
- Around line 804-809: The CSS rule using .sidebar-group-collapse-enter-to and
.sidebar-group-collapse-leave-from with a hard-coded max-height: 720px causes
clipping for tall groups; in WindowSideBar.vue replace this static cap by
removing the fixed max-height and implement Vue transition hooks (e.g.
beforeEnter/enter/afterEnter and beforeLeave/leave/afterLeave) on the transition
wrapping the group: in beforeEnter set el.style.maxHeight = '0px', in enter set
el.style.maxHeight = el.scrollHeight + 'px' and clear it in afterEnter, and for
collapse set el.style.maxHeight = el.scrollHeight + 'px' in beforeLeave then to
'0px' in leave and clear in afterLeave so the expand/collapse animates to the
element's actual height instead of 720px.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 08f5114a-0837-4f14-9e58-568ad84e0fee
📒 Files selected for processing (1)
src/renderer/src/components/WindowSideBar.vue
before:
before.mp4
after:
f37d6cb7762fb7e19b961cf73f046c99_raw.mp4
Summary by CodeRabbit