Compare commits

...
2 Commits
Author SHA1 Message Date
Christoph f0bd74be4e Merge pull request 'feat(history): make branch filter groups collapsible' (#28) from new-desgin into main
publish / Build and publish Windows installer (release) Failing after 13s
publish / Build and publish AUR packages (release) Canceled after 0s
publish / Build and publish Ubuntu AppImage (release) Canceled after 54s
Reviewed-on: #28
2026-08-23 21:28:46 +00:00
Christoph 79f4ec21e4 feat(history): make branch filter groups collapsible
Add collapsible local and remote branch groups to the branch
visibility dialog, including toggle buttons and selection counts.
Introduce comprehensive dialog styling and responsive rules to match
the app UI. Opening the dialog now defaults to local open and remote
closed for faster access.

- Add CSS for branch-filter dialog layout, theming, and responsiveness
- Implement group toggles with chevrons and visible selected counts
- Default to local group open and remote group closed on dialog open
2026-08-23 23:20:13 +02:00
2 changed files with 207 additions and 24 deletions
+149
View File
@@ -8523,3 +8523,152 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s
:root[data-appearance="custom"] .graph-gutter {
background: var(--color-surface-dim);
}
/* Branch visibility dialog ---------------------------------------------
Local and remote branches share one visual and interaction language. */
.branch-filter-backdrop,
:root[data-theme="light"] .branch-filter-backdrop {
padding: 24px;
background: var(--app-dialog-backdrop);
backdrop-filter: blur(5px);
}
.branch-filter-dialog,
:root[data-theme="light"] .branch-filter-dialog {
width: min(560px, 100%);
max-height: min(680px, calc(100vh - 48px));
border-color: var(--color-border);
background: var(--app-dialog-bg);
box-shadow: var(--app-dialog-shadow);
}
.branch-filter-dialog-head {
min-height: 64px;
padding: 12px 14px;
border-bottom-color: var(--color-border);
background: var(--app-dialog-chrome);
}
.branch-filter-dialog-head h3 {
color: var(--color-ink);
font-size: 15px;
}
.branch-filter-summary {
min-height: 48px;
padding: 8px 14px;
border-bottom-color: var(--color-border);
color: var(--color-ink-muted);
background: var(--color-surface-dim);
}
.branch-filter-actions button {
min-height: 28px;
border-color: var(--color-border);
color: var(--color-ink-muted);
background: var(--color-surface-raised);
}
.branch-filter-actions button:hover:not(:disabled) {
border-color: var(--color-border-input);
color: var(--color-ink);
background: var(--color-surface-hover);
}
.branch-filter-dialog-list {
gap: 8px;
padding: 10px;
background: var(--color-surface-alt);
}
.branch-filter-group {
display: grid;
min-width: 0;
overflow: hidden;
border: 1px solid var(--color-border);
background: var(--color-surface);
}
.branch-filter-group-toggle {
display: grid;
grid-template-columns: 16px minmax(0, 1fr) auto;
align-items: center;
gap: 8px;
width: 100%;
min-height: 36px;
padding: 0 10px;
border: 0;
color: var(--color-ink-muted);
background: var(--color-surface-dim);
text-align: left;
}
.branch-filter-group-toggle:hover:not(:disabled) {
color: var(--color-ink);
background: var(--color-surface-hover);
}
.branch-filter-group-toggle > svg {
color: var(--color-accent);
}
.branch-filter-group-toggle > span {
font-size: 10px;
font-weight: 850;
letter-spacing: 0.09em;
text-transform: uppercase;
}
.branch-filter-group-toggle > em {
min-width: 34px;
padding: 2px 5px;
border: 1px solid var(--color-border-subtle);
color: var(--color-ink-faint);
background: var(--color-surface-raised);
font-size: 9px;
font-style: normal;
font-weight: 800;
text-align: center;
}
.branch-filter-group-list {
display: grid;
gap: 4px;
padding: 6px;
border-top: 1px solid var(--color-border-subtle);
}
.branch-filter-option,
:root[data-theme="light"] .branch-filter-option,
:root[data-theme="light"] .branch-filter-option.remote {
min-height: 34px;
border-color: color-mix(in srgb, var(--color-accent) 32%, var(--color-border));
color: var(--color-ink);
background: color-mix(in srgb, var(--color-accent) 8%, var(--color-surface-raised));
opacity: 1;
}
.branch-filter-option:hover,
:root[data-theme="light"] .branch-filter-option:hover {
border-color: color-mix(in srgb, var(--color-accent) 52%, var(--color-border));
color: var(--color-ink);
background: color-mix(in srgb, var(--color-accent) 13%, var(--color-surface-hover));
}
.branch-filter-option.muted,
:root[data-theme="light"] .branch-filter-option.muted {
border-color: var(--color-border-subtle);
color: var(--color-ink-faint);
background: var(--color-surface-raised);
opacity: 0.72;
}
.branch-filter-option input {
accent-color: var(--color-accent);
}
@media (max-width: 560px) {
.branch-filter-backdrop { padding: 8px; }
.branch-filter-summary { align-items: stretch; flex-direction: column; }
.branch-filter-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
+58 -24
View File
@@ -117,6 +117,8 @@
let customVisibleBranches = $state<Set<string>>(new Set());
let loadedVisibilityRepository = $state("");
let branchDialogOpen = $state(false);
let localBranchGroupOpen = $state(true);
let remoteBranchGroupOpen = $state(false);
let expandedRefsCommitHash = $state("");
let panelElement = $state<HTMLElement | null>(null);
let contextCommit = $state<GitCommit | null>(null);
@@ -428,6 +430,8 @@
}
function openBranchDialog() {
localBranchGroupOpen = true;
remoteBranchGroupOpen = false;
branchDialogOpen = true;
}
@@ -1202,32 +1206,62 @@
<div class="branch-filter-dialog-list">
{#if localBranchNames.length > 0}
<span class="branch-filter-group-label">Local</span>
{#each localBranchNames as branch}
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option" title={branch}>
<input
type="checkbox"
checked={branchIsVisible(branch)}
onchange={() => toggleGraphBranch(branch)}
/>
<GitBranch size={14} aria-hidden="true" />
<span>{branch}</span>
</label>
{/each}
<section class="branch-filter-group">
<button
class="branch-filter-group-toggle"
type="button"
aria-expanded={localBranchGroupOpen}
onclick={() => { localBranchGroupOpen = !localBranchGroupOpen; }}
>
{#if localBranchGroupOpen}<ChevronDown size={14} aria-hidden="true" />{:else}<ChevronRight size={14} aria-hidden="true" />{/if}
<span>Local</span>
<em>{localBranchNames.filter(branchIsVisible).length}/{localBranchNames.length}</em>
</button>
{#if localBranchGroupOpen}
<div class="branch-filter-group-list">
{#each localBranchNames as branch}
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option" title={branch}>
<input
type="checkbox"
checked={branchIsVisible(branch)}
onchange={() => toggleGraphBranch(branch)}
/>
<GitBranch size={14} aria-hidden="true" />
<span>{branch}</span>
</label>
{/each}
</div>
{/if}
</section>
{/if}
{#if remoteBranchNames.length > 0}
<span class="branch-filter-group-label">Remote</span>
{#each remoteBranchNames as branch}
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option remote" title={branch}>
<input
type="checkbox"
checked={branchIsVisible(branch)}
onchange={() => toggleGraphBranch(branch)}
/>
<GitBranch size={14} aria-hidden="true" />
<span>{branch}</span>
</label>
{/each}
<section class="branch-filter-group">
<button
class="branch-filter-group-toggle"
type="button"
aria-expanded={remoteBranchGroupOpen}
onclick={() => { remoteBranchGroupOpen = !remoteBranchGroupOpen; }}
>
{#if remoteBranchGroupOpen}<ChevronDown size={14} aria-hidden="true" />{:else}<ChevronRight size={14} aria-hidden="true" />{/if}
<span>Remote</span>
<em>{remoteBranchNames.filter(branchIsVisible).length}/{remoteBranchNames.length}</em>
</button>
{#if remoteBranchGroupOpen}
<div class="branch-filter-group-list">
{#each remoteBranchNames as branch}
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option" title={branch}>
<input
type="checkbox"
checked={branchIsVisible(branch)}
onchange={() => toggleGraphBranch(branch)}
/>
<GitBranch size={14} aria-hidden="true" />
<span>{branch}</span>
</label>
{/each}
</div>
{/if}
</section>
{/if}
</div>
</div>