Polish vault inline panel transitions and compact header control#670
Polish vault inline panel transitions and compact header control#670busiqiao wants to merge 14 commits intobinaricat:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Refines the Vault/Hosts inline editing layout by synchronizing inline aside panel transitions with main content resizing and compacting the Hosts header controls while the side panel is open.
Changes:
- Reworked inline aside panel animation to use a mounted/visible state with CSS transitions (instead of enter keyframes) for smoother open/close.
- Updated VaultView Hosts header layout (search + connect action, compact control sizing) and adjusted grid sizing behavior.
- Plumbed an
openprop through host/group/serial detail panels and subpanels to support animated close.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| index.css | Replaces keyframe-based inline panel enter animation with data-state-driven width/flex-basis transitions. |
| components/ui/aside-panel.tsx | Adds inline panel presence management and data-state/aria-hidden wiring for inline layout transitions. |
| components/VaultView.tsx | Coordinates Hosts header compaction, host panel deferred presence, and panel open/close state handling. |
| components/HostDetailsPanel.tsx | Adds open prop and forwards it to AsidePanel and nested subpanels. |
| components/SerialHostDetailsPanel.tsx | Adds open prop and forwards it to AsidePanel. |
| components/GroupDetailsPanel.tsx | Adds open prop and forwards it to AsidePanel and nested subpanels. |
| components/host-details/ProxyPanel.tsx | Adds open prop and forwards it to AsidePanel. |
| components/host-details/EnvVarsPanel.tsx | Adds open prop and forwards it to AsidePanel. |
| components/host-details/CreateGroupPanel.tsx | Adds open prop and forwards it to AsidePanel. |
| components/host-details/ChainPanel.tsx | Adds open prop and forwards it to AsidePanel. |
| application/i18n/locales/en.ts | Updates Hosts search placeholder copy for quick-connect flow. |
| application/i18n/locales/zh-CN.ts | Updates Hosts search placeholder copy for quick-connect flow. |
| .gitignore | Ignores .omx/. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
components/VaultView.tsx
Outdated
| return () => { | ||
| window.clearTimeout(timeoutId); | ||
| }; | ||
| }, [open]); |
There was a problem hiding this comment.
The useDeferredPanelPresence effect references INLINE_ASIDE_PANEL_ANIMATION_MS but it isn’t included in the dependency array. With react-hooks/exhaustive-deps enabled (warn) and --max-warnings=0, this will fail lint. Add the constant to the deps array or add a justified eslint-disable-next-line react-hooks/exhaustive-deps for this effect.
| }, [open]); | |
| }, [open, INLINE_ASIDE_PANEL_ANIMATION_MS]); |
There was a problem hiding this comment.
Leaving this as-is.
INLINE_ASIDE_PANEL_ANIMATION_MS is a module-level constant, not a reactive value, so including it in the dependency array is not necessary here. The branch still passes:
npm run lint -- --max-warnings=0npm run build
components/VaultView.tsx
Outdated
| const handleEditGroupConfig = useCallback((groupPath: string) => { | ||
| clearPendingHostPanelReset(); | ||
| setIsHostPanelOpen(false); | ||
| setEditingHost(null); | ||
| clearHostPanelDraft(); | ||
| setEditingGroupPath(groupPath); |
There was a problem hiding this comment.
handleEditGroupConfig clears editingHost/newHostGroupPath immediately via clearHostPanelDraft(). Because the host panel render condition depends on editingHost, this can unmount the host panel immediately and prevent the close animation/presence logic from running. Consider calling closeHostPanel() (or otherwise deferring draft clearing until after INLINE_ASIDE_PANEL_ANIMATION_MS) before opening the group panel.
There was a problem hiding this comment.
Fixed in d7c33ae by deferring the host-to-group transition so the host panel can finish closing before the group panel opens.
| {/* Group Details Panel */} | ||
| {currentSection === "hosts" && isGroupPanelOpen && editingGroupPath && ( | ||
| <GroupDetailsPanel | ||
| open={isGroupPanelOpen} | ||
| key={editingGroupPath} | ||
| groupPath={editingGroupPath} |
There was a problem hiding this comment.
GroupDetailsPanel is conditionally rendered only when isGroupPanelOpen is true, so when it closes it unmounts immediately and the new open prop / AsidePanel close transition can’t run. If you want the close animation to complete, keep the panel mounted while open={false} (e.g., a deferred-presence boolean + delayed clearing of editingGroupPath).
There was a problem hiding this comment.
Fixed in d7c33ae by keeping the group details panel mounted long enough for the inline close transition to complete, instead of unmounting immediately when isGroupPanelOpen flips to false.
components/VaultView.tsx
Outdated
| @@ -2019,8 +2134,7 @@ | |||
| viewMode === "grid" | |||
| ? cn( | |||
| "grid gap-3", | |||
| !hasHostsSidePanel && "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", | |||
| ) | |||
| ) | |||
| : "flex flex-col gap-0", | |||
There was a problem hiding this comment.
This grid className now calls cn("grid gap-3",) with only a single argument and a dangling comma/newline, which looks like an accidental leftover from removing the conditional grid-cols-* classes. Consider simplifying to just "grid gap-3" (and apply the same cleanup to the other similar sections).
There was a problem hiding this comment.
Cleaned up in d7c33ae for readability
components/VaultView.tsx
Outdated
| @@ -2123,8 +2237,7 @@ | |||
| viewMode === "grid" | |||
| ? cn( | |||
| "grid gap-3", | |||
| !hasHostsSidePanel && "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", | |||
| ) | |||
| ) | |||
| : "flex flex-col gap-0", | |||
There was a problem hiding this comment.
This grid className now calls cn("grid gap-3",) with only a single argument and a dangling comma/newline, which looks like an accidental leftover from removing the conditional grid-cols-* classes. Consider simplifying to just "grid gap-3".
There was a problem hiding this comment.
Cleaned up in d7c33ae for readability
| className={cn( | ||
| displayedGroups.length === 0 ? "hidden" : "", | ||
| viewMode === "grid" | ||
| ? cn( | ||
| "grid gap-3", | ||
| !hasHostsSidePanel && "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", | ||
| ) | ||
| ) | ||
| : "flex flex-col gap-0", |
There was a problem hiding this comment.
This grid className now calls cn("grid gap-3",) with only a single argument and a dangling comma/newline, which looks like an accidental leftover from removing the conditional grid-cols-* classes. Consider simplifying to just "grid gap-3".
There was a problem hiding this comment.
Cleaned up in d7c33ae for readability
| className={cn( | ||
| viewMode === "grid" | ||
| ? cn( | ||
| "grid gap-3", | ||
| !hasHostsSidePanel && "grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4", | ||
| ) | ||
| ) | ||
| : "flex flex-col gap-0", |
There was a problem hiding this comment.
This grid className now calls cn("grid gap-3",) with only a single argument and a dangling comma/newline. Consider simplifying to just "grid gap-3" for readability.
There was a problem hiding this comment.
Cleaned up in d7c33ae for readability
components/ui/aside-panel.tsx
Outdated
| window.clearTimeout(timeoutId); | ||
| } | ||
| }; | ||
| }, [isInline, isMounted, open]); |
There was a problem hiding this comment.
useInlinePanelPresence references INLINE_ASIDE_PANEL_ANIMATION_MS inside the effect but it isn’t included in the dependency array. With react-hooks/exhaustive-deps enabled and --max-warnings=0, this will fail lint. Add the constant to the deps array or add a justified eslint-disable-next-line react-hooks/exhaustive-deps.
| }, [isInline, isMounted, open]); | |
| }, [INLINE_ASIDE_PANEL_ANIMATION_MS, isInline, isMounted, open]); |
There was a problem hiding this comment.
Leaving this as-is.
INLINE_ASIDE_PANEL_ANIMATION_MS is a module-level constant, not a reactive value, so including it in the dependency array is not necessary here. The branch still passes:
npm run lint -- --max-warnings=0npm run build
The hosts vault now keeps the inline editor, header controls, and content width changes on a single visual rhythm so editing feels more intentional instead of jumpy. The compact header state now preserves all actions in one row, the search field uses an inline Connect action, and inline panels stay mounted long enough to animate closed cleanly. Constraint: Must preserve the existing inline editing workflow and minimum window width Constraint: No new dependencies or window-manager changes for this pass Rejected: Keep delayed grid relayout logic | caused visible desync gaps during close Rejected: Two-row header in compact mode | created unstable layout shifts across panel states Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Keep header compacting tied to real side-panel state; avoid reintroducing independent delayed layout toggles without visual verification Tested: npm run lint -- --max-warnings=0 Tested: npm run build Not-tested: Manual visual QA on native desktop (only WSLg-driven iteration)
The vault editor transitions now keep group details mounted long enough for close animations to finish, and host-to-group handoff no longer clears host panel state before the exit motion can run. While touching that area, the temporary grid class leftovers were simplified for readability. Constraint: Must keep the current inline panel animation model and branch scope intact Rejected: Ignore the review comments | would leave the group panel close animation effectively broken Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep deferred-presence logic paired with deferred draft clearing whenever inline panel visibility depends on draft state Tested: npm run lint -- --max-warnings=0 Tested: npm run build Not-tested: Manual visual regression pass after the follow-up fix
The follow-up panel-presence fix referenced group-panel cleanup callbacks before they were initialized, which caused VaultView to throw during render and left the hosts page blank. Reordering that cleanup effect and keeping the related panel-transition fixes intact restores the view while preserving the intended close-animation behavior. Constraint: Must preserve the review-follow-up animation fixes while removing the runtime initialization fault Rejected: Revert the group-panel transition work entirely | would throw away the substantive review fixes unnecessarily Confidence: high Scope-risk: narrow Reversibility: clean Directive: In this component, do not add effects that reference callback const bindings before those callbacks are declared Tested: npm run lint -- --max-warnings=0 Tested: npm run build Tested: Playwright page load on local Vite dev server Not-tested: Manual end-to-end interaction pass after this hotfix
Tighten the Vault inline editor sizing and smooth the shared header/ panel behavior so the editor feels more coherent during open/close. Constraint: Must preserve the current Vault inline editing workflow Confidence: medium Scope-risk: narrow Tested: npm run lint -- --max-warnings=0 Tested: npm run build
Adjust the Vault inline panel width and motion timing to better match the latest UI tuning, including header compact timing during panel close. Constraint: Must preserve the current Vault split-style interaction model Confidence: medium Scope-risk: narrow Tested: npm run lint -- --max-warnings=0 Tested: npm run build
The inline vault details panels now avoid double-render overlap during Host/Group switches while preserving the normal open animation for non-swap sidebar opens. The host grid freeze behavior also keeps cards and grouped dividers visually stable during sidebar resizing without clipping card shadows. Constraint: Group Details and Host Details share one visual lane but are still separate panel components Constraint: Sidebar open animation must remain intact for ordinary opens outside detail-panel swaps Rejected: Always disable initial inline animation | broke normal sidebar pop-out motion Rejected: Use extra padding/margins during frozen grid resize | caused cards to twitch at animation start Confidence: medium Scope-risk: moderate Reversibility: clean Directive: Keep swap-specific animation bypass tightly scoped to Group↔Host transitions only Tested: npx eslint components/VaultView.tsx components/ui/aside-panel.tsx components/GroupDetailsPanel.tsx components/HostDetailsPanel.tsx components/SerialHostDetailsPanel.tsx Tested: npm run lint Not-tested: Manual UI verification across all vault view/sort-mode combinations
The hosts header now holds its compact layout steady while the inline sidebar resize freeze is active, avoiding a second relayout at the moment the panel animation starts. This keeps the sidebar expansion smoother without changing the detail-panel swap behavior. Constraint: Header compactness must still track side-panel presence after the resize settles Rejected: Leave header compact state fully live during resize | caused a visible hitch at animation start Confidence: medium Scope-risk: narrow Reversibility: clean Directive: During sidebar freeze windows, avoid introducing new header relayout triggers Tested: npx eslint components/VaultView.tsx Tested: npm run lint Not-tested: Manual performance comparison on low-end hardware
Slow the Vault details panel motion and align the visual layer with the layout shell so opening and closing can be inspected without the old blank-gap artifact. The panel root now owns only width changes while the inner visual layer owns border, background, and shadow timing. Constraint: User needs a slow-motion build to inspect drawer behavior before further animation work Rejected: Keep the fast timing while debugging | too hard to observe phase ordering reliably Confidence: medium Scope-risk: moderate Reversibility: clean Directive: The current 1200ms timing is intentionally slow for observation — retune after motion behavior is confirmed Tested: eslint on changed panel files; npm run build; manual host/group drawer smoke via preview Not-tested: host↔group switching and serial edit drawer path
The first attempt to keep cards live during drawer motion used observer-driven relayout compensation and caused disappearing content plus sustained jitter in Hosts. This commit removes that unstable path and returns VaultView to a stable baseline so the non-freezing design can be reintroduced with a safer two-state approach. Constraint: User explicitly reported continuous jitter and content loss after opening the inline drawer Rejected: Keep iterating on the observer-driven approach | too unstable to preserve as a baseline Confidence: high Scope-risk: narrow Reversibility: clean Directive: Reintroduce non-freezing card motion only with bounded state transitions, not continuous resize-driven replay Tested: npm run build; preview boot smoke; host and group panel open/close smoke after rollback Not-tested: host↔group switching and serial edit drawer path
The Hosts page drawer animation now keeps the inline panel, host cards, group cards, and related chrome on a shared 1100ms timeline. Card motion uses stable start/end snapshots with unique card IDs so repeated hosts in Recent/Main sections do not borrow the wrong geometry, while already-open Host/Group panel switches stay at full width and only replace content. Constraint: Right details panels use a 340px inline width and Hosts content padding changes while the panel is active Rejected: Continuous ResizeObserver-driven animation | previously caused disappearing content and layout thrash Rejected: Keyframe-driven ghost launch rewrite | regressed the already-stable opening behavior Confidence: medium Scope-risk: moderate Directive: Keep Host/Group same-width switches out of opening/closing phases; only closed-to-open transitions should animate panel width Tested: npx eslint components/VaultView.tsx components/ui/aside-panel.tsx Tested: npm run build Tested: Playwright smoke for Host open/close, Group open/close, and Group Details to Host Details switch Not-tested: Subjective frame pacing on low-end GPUs
Remove dual width+flex-basis CSS transition that caused transitionend to fire prematurely when one property completed before the other, producing a visible snap at the end of the drawer slide-in. Switch flex-basis to auto so it naturally follows the transitioning width value at every frame, eliminating the synchronization issue entirely.
All synchronized timing values updated consistently across CSS transitions, header compact/expand, content padding, and JS timers.
Extend ghost card measurement system to also capture per-grid heights
for pinned, recent, and groups grids. Apply transitioning fixed
heights to these secondary grids during the sidebar animation so
section titles ("Pinned", "Recently Connected", "Groups", "Hosts")
shift smoothly instead of jumping.
6508169 to
f71d387
Compare
|
@binaricat 点击编辑按钮弹出的右侧侧边栏的弹出和关闭动画我优化了好几版,目前这版感觉还行,辛苦检查一下效果和代码 |
Summary
This PR refines the Hosts/Vault editing experience by making the inline side panel and main content transitions feel more coherent, while keeping the top toolbar usable within the existing minimum window width.
What Changed
Notes
Testing
npm run lint -- --max-warnings=0npm run build