feat(ui): enhance panel resizing and state persistence

This update introduces functionality for resizing various panels in the UI, including the left sidebar, branch panel, stash panel, and file history. It also adds state persistence for panel sizes and collapsed states using local storage, improving user experience by maintaining preferences across sessions.

- Implemented resizing functionality for multiple UI panels
- Added local storage support for panel dimensions and collapsed states
- Enhanced accessibility features for panel controls
This commit is contained in:
Christoph Brandau
2026-07-09 08:33:09 +02:00
parent adfb5f4158
commit 8627012d93
6 changed files with 749 additions and 75 deletions
+217 -36
View File
@@ -1096,63 +1096,120 @@
.workspace {
display: grid;
grid-template-columns: clamp(220px, 18vw, 280px) minmax(0, 1fr) minmax(560px, var(--history-aside-width, 620px));
grid-template-columns: minmax(220px, var(--left-sidebar-width, 280px)) 8px minmax(0, 1fr) 8px minmax(560px, var(--history-aside-width, 620px));
flex: 1 1 0;
min-width: 0;
min-height: 0;
gap: 8px;
column-gap: 0;
row-gap: 8px;
}
.history-aside {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) 300px;
grid-template-columns: minmax(0, 1fr) 8px minmax(240px, var(--file-history-width, 300px));
min-width: 0;
min-height: 0;
gap: 8px;
column-gap: 0;
row-gap: 8px;
}
.history-resize-handle {
position: absolute;
z-index: 20;
top: 0;
bottom: 0;
left: -7px;
width: 12px;
.history-aside.file-history-collapsed {
grid-template-columns: minmax(0, 1fr) 0 42px;
}
.left-sidebar-resize-handle,
.history-resize-handle,
.file-history-resize-handle {
position: relative;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
min-width: 8px;
cursor: col-resize;
touch-action: none;
}
.history-resize-handle::before {
.left-sidebar-resize-handle::before,
.history-resize-handle::before,
.file-history-resize-handle::before {
content: "";
position: absolute;
top: 12px;
bottom: 12px;
left: 5px;
width: 2px;
width: 3px;
height: 40px;
border-radius: 999px;
background: transparent;
transition: background 120ms ease, box-shadow 120ms ease;
background: var(--color-border);
transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
.left-sidebar-resize-handle:hover::before,
.left-sidebar-resize-handle.resizing::before,
.history-resize-handle:hover::before,
.history-resize-handle.resizing::before {
background: rgba(65,209,255,0.62);
.history-resize-handle.resizing::before,
.file-history-resize-handle:hover::before,
.file-history-resize-handle.resizing::before {
background: var(--color-accent);
box-shadow: 0 0 14px rgba(65,209,255,0.3);
}
.history-resize-handle:focus-visible {
.left-sidebar-resize-handle:focus-visible,
.history-resize-handle:focus-visible,
.file-history-resize-handle:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.history-aside.file-history-collapsed .file-history-resize-handle {
pointer-events: none;
}
.history-aside.file-history-collapsed .file-history-resize-handle::before {
display: none;
}
.left-sidebar {
display: grid;
grid-template-rows: minmax(170px, 0.75fr) minmax(150px, 0.55fr) minmax(220px, 1fr);
grid-template-rows: minmax(180px, var(--branch-panel-height, 260px)) 14px minmax(150px, var(--stash-panel-height, 190px)) 14px minmax(220px, 1fr);
align-content: start;
min-width: 0;
min-height: 0;
gap: 8px;
gap: 0;
}
.left-panel-resize-handle {
min-height: 14px;
margin: 0;
}
.left-panel-resize-placeholder {
display: block;
min-height: 0;
min-width: 0;
}
.left-sidebar.branch-collapsed {
grid-template-rows: auto 0 minmax(150px, var(--stash-panel-height, 190px)) 14px minmax(220px, 1fr);
}
.left-sidebar.explorer-collapsed {
grid-template-rows: minmax(180px, var(--branch-panel-height, 260px)) 14px minmax(150px, var(--stash-panel-height, 190px)) 0 auto;
}
.left-sidebar.branch-collapsed.explorer-collapsed {
grid-template-rows: auto 0 minmax(150px, var(--stash-panel-height, 190px)) 0 auto;
}
.left-sidebar:has(.stash-panel.collapsed) {
grid-template-rows: minmax(170px, 0.85fr) auto minmax(220px, 1.15fr);
grid-template-rows: minmax(180px, var(--branch-panel-height, 260px)) 14px auto 0 minmax(220px, 1fr);
}
.left-sidebar.branch-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: auto 0 auto 0 minmax(220px, 1fr);
}
.left-sidebar.explorer-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: minmax(180px, var(--branch-panel-height, 260px)) 14px auto 0 auto;
}
.left-sidebar.branch-collapsed.explorer-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: auto 0 auto 0 auto;
align-content: start;
}
/* --- Main panel --- */
@@ -1474,6 +1531,9 @@
gap: 6px;
}
.branch-panel { position: relative; }
.branch-panel.collapsed {
grid-template-rows: auto;
}
.branch-create-toggle {
width: 26px;
@@ -1747,6 +1807,9 @@
/* --- Explorer --- */
.explorer-panel { position: relative; }
.explorer-panel.collapsed {
grid-template-rows: auto;
}
.explorer-head-actions { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; }
.explorer-bulk-button {
@@ -2127,8 +2190,75 @@
background: rgba(65,209,255,0.08);
color: var(--color-ink);
}
.file-history-panel.collapsed {
display: grid;
grid-template-rows: 1fr;
overflow: hidden;
}
.file-history-rail-toggle {
display: grid;
grid-template-rows: auto minmax(0, 1fr) auto;
justify-items: center;
gap: 8px;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
padding: 12px 0;
border: 0;
border-radius: 0;
color: var(--color-ink-dim);
background: linear-gradient(180deg, rgba(100,108,255,0.12), rgba(65,209,255,0.04));
}
.file-history-rail-toggle:hover:not(:disabled) {
color: var(--color-ink);
background: linear-gradient(180deg, rgba(100,108,255,0.18), rgba(65,209,255,0.08));
}
.file-history-rail-toggle span {
writing-mode: vertical-rl;
text-orientation: mixed;
overflow: hidden;
color: currentColor;
font-size: 11px;
font-weight: 800;
letter-spacing: 0.04em;
text-transform: uppercase;
white-space: nowrap;
}
.file-history-rail-toggle small {
display: inline-grid;
place-items: center;
min-width: 22px;
min-height: 22px;
border-radius: 999px;
color: var(--color-ink);
background: rgba(94,110,156,0.18);
font-size: 11px;
font-weight: 800;
}
.file-history-head { align-items: flex-start; }
.file-history-heading { min-width: 0; flex: 1 1 auto; overflow: hidden; }
.file-history-head-actions {
display: flex;
align-items: center;
flex: 0 0 auto;
gap: 6px;
}
.file-history-toggle {
width: 26px;
min-width: 26px;
min-height: 26px;
padding: 0;
border-color: rgba(65,209,255,0.2);
border-radius: 7px;
color: var(--color-ink-dim);
background: rgba(65,209,255,0.06);
}
.file-history-toggle:hover:not(:disabled) {
border-color: rgba(65,209,255,0.45);
color: #ffffff;
background: rgba(65,209,255,0.13);
}
.section-head .file-history-name {
display: -webkit-box;
max-height: 2.4em;
@@ -4261,27 +4391,52 @@
/* --- Responsive breakpoints --- */
@media (min-width: 1800px) {
.workspace { grid-template-columns: 320px minmax(0, 1fr) minmax(560px, var(--history-aside-width, 680px)); }
.workspace { grid-template-columns: minmax(220px, var(--left-sidebar-width, 320px)) 8px minmax(0, 1fr) 8px minmax(560px, var(--history-aside-width, 680px)); }
}
@media (max-width: 1400px) {
.workspace { grid-template-columns: clamp(200px, 17vw, 265px) minmax(0, 1fr) minmax(540px, var(--history-aside-width, 580px)); }
.workspace { grid-template-columns: minmax(200px, var(--left-sidebar-width, 265px)) 8px minmax(0, 1fr) 8px minmax(540px, var(--history-aside-width, 580px)); }
}
/* Stack CommitPanel below StatusPanel; history panels stay side by side */
@media (max-width: 1100px) {
.workspace { grid-template-columns: clamp(185px, 16vw, 220px) minmax(0, 1fr) minmax(500px, var(--history-aside-width, 560px)); }
.workspace { grid-template-columns: minmax(185px, var(--left-sidebar-width, 220px)) 8px minmax(0, 1fr) 8px minmax(500px, var(--history-aside-width, 560px)); }
.top-section { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) 14px var(--commit-panel-height, 190px); }
}
/* Compact: stack history panels vertically, narrow sidebars */
@media (max-width: 960px) {
.workspace { grid-template-columns: 180px minmax(0, 1fr) 300px; gap: 6px; }
.history-aside { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1.3fr) minmax(0, 0.7fr); }
.history-resize-handle { display: none; }
.workspace { grid-template-columns: 180px 8px minmax(0, 1fr) 0 300px; row-gap: 6px; }
.history-aside { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1.3fr) minmax(0, 0.7fr); row-gap: 6px; }
.history-aside.file-history-collapsed { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) 42px; }
.history-resize-handle,
.file-history-resize-handle { display: none; }
.shell-body { gap: 6px; }
.left-sidebar { gap: 6px; grid-template-rows: minmax(150px, 0.7fr) minmax(145px, 0.55fr) minmax(190px, 1fr); }
.left-sidebar:has(.stash-panel.collapsed) { grid-template-rows: minmax(150px, 0.8fr) auto minmax(190px, 1.1fr); }
.left-sidebar {
grid-template-rows: minmax(180px, var(--branch-panel-height, 230px)) 14px minmax(150px, var(--stash-panel-height, 170px)) 14px minmax(220px, 1fr);
}
.left-sidebar:has(.stash-panel.collapsed) {
grid-template-rows: minmax(180px, var(--branch-panel-height, 230px)) 14px auto 0 minmax(220px, 1fr);
}
.left-sidebar.branch-collapsed {
grid-template-rows: auto 0 minmax(150px, var(--stash-panel-height, 170px)) 14px minmax(220px, 1fr);
}
.left-sidebar.explorer-collapsed {
grid-template-rows: minmax(180px, var(--branch-panel-height, 230px)) 14px minmax(150px, var(--stash-panel-height, 170px)) 0 auto;
}
.left-sidebar.branch-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: auto 0 auto 0 minmax(220px, 1fr);
}
.left-sidebar.explorer-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: minmax(180px, var(--branch-panel-height, 230px)) 14px auto 0 auto;
}
.left-sidebar.branch-collapsed.explorer-collapsed {
grid-template-rows: auto 0 minmax(150px, var(--stash-panel-height, 170px)) 0 auto;
}
.left-sidebar.branch-collapsed.explorer-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: auto 0 auto 0 auto;
align-content: start;
}
.section-head { min-height: 40px; padding: 6px 10px; }
.repo-summary { height: 40px; padding: 0 10px; }
.repo-branch { max-width: 160px; }
@@ -4297,9 +4452,35 @@
body { overflow: auto; }
.shell-body { min-height: 100%; gap: 6px; }
.workspace { grid-template-columns: 1fr; gap: 6px; }
.left-sidebar-resize-handle { display: none; }
.history-aside { grid-template-columns: 1fr; grid-template-rows: minmax(200px, 1fr) minmax(150px, 0.5fr); min-height: 380px; }
.left-sidebar { grid-template-rows: minmax(180px, 0.9fr) minmax(150px, 0.55fr) minmax(220px, 1fr); min-height: 560px; }
.left-sidebar:has(.stash-panel.collapsed) { grid-template-rows: minmax(180px, 1fr) auto minmax(220px, 1.1fr); }
.history-aside.file-history-collapsed { grid-template-rows: minmax(200px, 1fr) 42px; }
.left-sidebar {
grid-template-rows: minmax(180px, var(--branch-panel-height, 240px)) 14px minmax(150px, var(--stash-panel-height, 180px)) 14px minmax(220px, 1fr);
min-height: 560px;
}
.left-sidebar:has(.stash-panel.collapsed) {
grid-template-rows: minmax(180px, var(--branch-panel-height, 240px)) 14px auto 0 minmax(220px, 1fr);
}
.left-sidebar.branch-collapsed {
grid-template-rows: auto 0 minmax(150px, var(--stash-panel-height, 180px)) 14px minmax(220px, 1fr);
}
.left-sidebar.explorer-collapsed {
grid-template-rows: minmax(120px, var(--branch-panel-height, 240px)) 14px minmax(150px, var(--stash-panel-height, 180px)) 0 auto;
}
.left-sidebar.branch-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: auto 0 auto 0 minmax(0, 1fr);
}
.left-sidebar.explorer-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: minmax(120px, var(--branch-panel-height, 240px)) 14px auto 0 auto;
}
.left-sidebar.branch-collapsed.explorer-collapsed {
grid-template-rows: auto 0 minmax(150px, var(--stash-panel-height, 180px)) 0 auto;
}
.left-sidebar.branch-collapsed.explorer-collapsed:has(.stash-panel.collapsed) {
grid-template-rows: auto 0 auto 0 auto;
align-content: start;
}
.top-section { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) 14px var(--commit-panel-height, 190px); }
.repo-form { grid-template-columns: 1fr; }
.repo-tabbar { grid-template-columns: minmax(0, 1fr) auto; }
@@ -4322,7 +4503,7 @@
.dialog-body { grid-template-columns: 1fr; grid-template-rows: minmax(120px, 0.4fr) minmax(0, 1fr); }
.compare-dialog .dialog-body { grid-template-columns: 1fr; grid-template-rows: minmax(120px, 0.4fr) minmax(0, 1fr); }
.global-search-options { grid-template-columns: 1fr; }
.file-search-split { grid-template-columns: 1fr; grid-template-rows: minmax(150px, 0.9fr) minmax(220px, 1fr); }
.file-search-split { grid-template-columns: 1fr; grid-template-rows: minmax(150px, 0.9fr) minmax(0, 1fr); }
.file-search-column { border-right: none; border-bottom: 1px solid var(--color-border-subtle); }
.file-search-hit { grid-template-columns: auto minmax(0, 1fr); align-items: start; }
.file-search-hit .status-badge,