Compare commits

...
3 Commits
Author SHA1 Message Date
Christoph 4533f8aa38 style(theme): add dialog & panel CSS vars and refactor status panel styles
publish / Build and publish Ubuntu AppImage (release) Successful in 19m39s
publish / Build and publish Windows installer (release) Successful in 22m14s
publish / Build and publish AUR packages (release) Successful in 48m41s
Add CSS custom properties for dialog backdrop, shadows, and panel
highlights to centralize visual theming. Refactor the status panel
overlay to consume these tokens and use color-mix instead of hardcoded
rgba values. This improves visual consistency and lets overlays adapt
cleanly to appearance changes and theme variants.

- Centralize dialog and panel visuals with new CSS variables
- Replace fixed rgba values with tokenized colors and color-mix
- Move borders, shadows, and gradients to use the new theme tokens
2026-08-23 23:38:42 +02:00
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
4 changed files with 234 additions and 42 deletions
+5 -1
View File
@@ -1179,7 +1179,8 @@
root.dataset.appearance = next;
const customProperties = [
"--app-bg", "--app-button-bg", "--app-input-bg", "--app-dialog-bg", "--app-dialog-chrome",
"--app-settings-row-bg", "--color-surface", "--color-surface-alt", "--color-surface-dim",
"--app-dialog-backdrop", "--app-dialog-shadow", "--app-panel-shadow", "--app-settings-row-bg",
"--color-surface", "--color-surface-alt", "--color-surface-dim",
"--color-surface-hover", "--color-surface-raised", "--color-surface-solid", "--color-border",
"--color-border-subtle", "--color-border-input", "--color-primary", "--color-primary-dark",
"--color-accent", "--color-ink", "--color-ink-muted", "--color-ink-faint", "--color-ink-dim",
@@ -1195,6 +1196,9 @@
"--app-input-bg": `color-mix(in srgb, ${surface} 88%, white)`,
"--app-dialog-bg": surface,
"--app-dialog-chrome": `color-mix(in srgb, ${surface} 90%, ${background})`,
"--app-dialog-backdrop": `color-mix(in srgb, ${background} 72%, transparent)`,
"--app-dialog-shadow": `0 24px 68px color-mix(in srgb, ${text} 24%, transparent), 0 2px 12px color-mix(in srgb, ${text} 12%, transparent)`,
"--app-panel-shadow": `0 18px 48px color-mix(in srgb, ${text} 18%, transparent), inset 0 1px 0 color-mix(in srgb, ${surface} 88%, white)`,
"--app-settings-row-bg": `color-mix(in srgb, ${surface} 92%, ${background})`,
"--color-surface": surface,
"--color-surface-alt": `color-mix(in srgb, ${surface} 82%, ${background})`,
+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>
+22 -17
View File
@@ -660,8 +660,12 @@
display: grid;
place-items: center;
background:
radial-gradient(circle at 50% 38%, rgba(111, 140, 255, 0.1), transparent 55%),
rgba(7, 10, 16, 0.62);
radial-gradient(
circle at 50% 38%,
color-mix(in srgb, var(--color-accent) 13%, transparent),
transparent 55%
),
var(--app-dialog-backdrop);
backdrop-filter: blur(4px);
animation: status-panel-overlay-in 120ms ease;
}
@@ -673,13 +677,13 @@
gap: 12px;
width: min(300px, calc(100% - 32px));
padding: 26px 28px 26px;
border: 1px solid rgba(90, 111, 154, 0.28);
border: 1px solid var(--color-border-input);
border-radius: 16px;
background:
linear-gradient(180deg, rgba(23, 29, 43, 0.92), rgba(12, 17, 27, 0.94)),
var(--color-surface-raised);
var(--app-panel-highlight),
var(--app-dialog-bg);
color: var(--color-ink);
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.34), inset 0 1px 0 rgba(255, 255, 255, 0.05);
box-shadow: var(--app-dialog-shadow);
}
.status-panel-overlay-mark {
@@ -694,7 +698,7 @@
.status-panel-overlay-halo {
position: absolute;
inset: 6px;
border: 1px solid rgba(111, 140, 255, 0.22);
border: 1px solid color-mix(in srgb, var(--color-primary) 34%, transparent);
border-radius: 22px;
transform: rotate(45deg);
}
@@ -703,7 +707,7 @@
}
.status-panel-overlay-halo.halo-two {
inset: 16px;
border-color: rgba(77, 182, 214, 0.24);
border-color: color-mix(in srgb, var(--color-accent) 38%, transparent);
animation: status-panel-overlay-halo-breathe 2.4s ease-in-out infinite reverse;
}
@@ -721,16 +725,16 @@
stroke-linecap: round;
stroke-dasharray: 165;
stroke-dashoffset: 165;
filter: drop-shadow(0 0 6px rgba(77, 182, 214, 0.32));
filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-accent) 38%, transparent));
animation: status-panel-overlay-trace-draw 2.6s ease-in-out infinite;
}
.status-panel-overlay-traces .trace-main { stroke: #6f8cff; }
.status-panel-overlay-traces .trace-main { stroke: var(--color-primary); }
.status-panel-overlay-traces .trace-branch {
stroke: #4db6d6;
stroke: var(--color-accent);
animation-delay: 0.28s;
}
.status-panel-overlay-traces .trace-cut {
stroke: rgba(177, 186, 208, 0.42);
stroke: color-mix(in srgb, var(--color-ink-muted) 52%, transparent);
stroke-dasharray: 128;
stroke-dashoffset: 128;
animation-delay: 0.55s;
@@ -743,8 +747,8 @@
height: 64px;
object-fit: contain;
filter:
drop-shadow(0 8px 12px rgba(0, 0, 0, 0.5))
drop-shadow(0 0 10px rgba(77, 182, 214, 0.2));
drop-shadow(0 8px 12px color-mix(in srgb, var(--color-ink) 24%, transparent))
drop-shadow(0 0 10px color-mix(in srgb, var(--color-accent) 24%, transparent));
animation: status-panel-overlay-icon-float 2.4s ease-in-out infinite;
}
@@ -765,15 +769,16 @@
height: 4px;
overflow: hidden;
border-radius: 999px;
background: rgba(111, 140, 255, 0.14);
border: 1px solid var(--color-border-subtle);
background: color-mix(in srgb, var(--color-primary) 12%, var(--color-surface-dim));
}
.status-panel-overlay-bar span {
position: absolute;
inset: 0;
width: 46%;
border-radius: inherit;
background: linear-gradient(90deg, transparent, #6f8cff 40%, #4db6d6 74%, transparent);
box-shadow: 0 0 12px rgba(77, 182, 214, 0.26);
background: linear-gradient(90deg, transparent, var(--color-primary) 40%, var(--color-accent) 74%, transparent);
box-shadow: 0 0 12px color-mix(in srgb, var(--color-accent) 32%, transparent);
animation: status-panel-overlay-bar-slide 1.35s ease-in-out infinite;
}