Skip to content

Commit faa2534

Browse files
committed
Add striped rows setting for file lists
- New `listing.stripedRows` boolean setting (off by default) in Listing section - Alternates row shading in both Full and Brief view modes using data index (stable under virtual scrolling) - Uses `color-mix()` against existing tokens so it auto-adapts to light/dark mode
1 parent 90b5ea0 commit faa2534

7 files changed

Lines changed: 54 additions & 1 deletion

File tree

apps/desktop/src/app.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
--color-toast-success-stripe: #16a34a;
8585
--color-toast-success-bg: #f0fdf4;
8686

87+
/* === Striped rows (zebra striping for file lists) === */
88+
--color-bg-stripe: color-mix(in oklch, var(--color-bg-primary), var(--color-text-primary) 4%);
89+
8790
/* === Selection === */
8891
--color-selection-fg: #c9a227;
8992

apps/desktop/src/lib/file-explorer/views/BriefList.svelte

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
updateIndexSizesInPlace,
2020
} from './file-list-utils'
2121
import { buildDirSizeTooltip, hasSizeMismatch } from './full-list-utils'
22-
import { getRowHeight, formatFileSize, getSizeMismatchWarning } from '$lib/settings/reactive-settings.svelte'
22+
import { getRowHeight, formatFileSize, getSizeMismatchWarning, getStripedRows } from '$lib/settings/reactive-settings.svelte'
2323
import { getSetting } from '$lib/settings/settings-store'
2424
import { formatNumber, pluralize } from '../selection/selection-info-utils'
2525
import { isScanning, isAggregating } from '$lib/indexing/index-state.svelte'
@@ -435,6 +435,9 @@
435435
// Size mismatch warning setting
436436
const showSizeMismatchWarning = $derived(getSizeMismatchWarning())
437437
438+
// Striped rows setting
439+
const stripedRows = $derived(getStripedRows())
440+
438441
/** Build tooltip for a directory entry showing recursive size info. */
439442
function buildDirTooltip(file: FileEntry): string | { html: string } | undefined {
440443
if (!file.isDirectory) return undefined
@@ -536,6 +539,7 @@
536539
class="file-entry"
537540
class:is-under-cursor={globalIndex === cursorIndex}
538541
class:is-selected={selectedIndices.has(globalIndex)}
542+
class:is-striped={stripedRows && globalIndex % 2 === 1}
539543
data-drop-target-path={file.isDirectory && file.name !== '..' ? file.path : undefined}
540544
use:tooltip={buildDirTooltip(file)}
541545
style="height: {rowHeight}px;"
@@ -650,6 +654,10 @@
650654
overflow: hidden;
651655
}
652656
657+
.file-entry.is-striped {
658+
background-color: var(--color-bg-stripe);
659+
}
660+
653661
.file-entry.is-under-cursor {
654662
background-color: var(--color-cursor-inactive);
655663
}

apps/desktop/src/lib/file-explorer/views/FullList.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
formatFileSize,
3636
getSizeDisplayMode,
3737
getSizeMismatchWarning,
38+
getStripedRows,
3839
} from '$lib/settings/reactive-settings.svelte'
3940
import { iconCacheCleared } from '$lib/icon-cache'
4041
import { tooltip } from '$lib/tooltip/tooltip'
@@ -134,6 +135,9 @@
134135
// Size mismatch warning setting
135136
const showSizeMismatchWarning = $derived(getSizeMismatchWarning())
136137
138+
// Striped rows setting
139+
const stripedRows = $derived(getStripedRows())
140+
137141
// Drive index state — show spinner while scanning OR aggregating (sizes aren't ready until aggregation finishes)
138142
const indexing = $derived(isScanning() || isAggregating())
139143
@@ -445,6 +449,7 @@
445449
class="file-entry"
446450
class:is-under-cursor={globalIndex === cursorIndex}
447451
class:is-selected={selectedIndices.has(globalIndex)}
452+
class:is-striped={stripedRows && globalIndex % 2 === 1}
448453
data-drop-target-path={file.isDirectory && file.name !== '..' ? file.path : undefined}
449454
style="height: {rowHeight}px; grid-template-columns: 16px 1fr 60px 115px {dateColumnWidth}px;"
450455
onmousedown={(e: MouseEvent) => {
@@ -609,6 +614,10 @@
609614
padding-bottom: var(--spacing-xs);
610615
}
611616
617+
.file-entry.is-striped {
618+
background-color: var(--color-bg-stripe);
619+
}
620+
612621
.file-entry.is-under-cursor {
613622
background-color: var(--color-cursor-inactive);
614623
}

apps/desktop/src/lib/settings/reactive-settings.svelte.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ let directorySortMode = $state<DirectorySortMode>('likeFiles')
3131
let appColor = $state<AppColor>('cmdr-gold')
3232
let sizeDisplay = $state<SizeDisplayMode>('smart')
3333
let sizeMismatchWarning = $state<boolean>(true)
34+
let stripedRows = $state<boolean>(false)
3435

3536
let initialized = false
3637
let unsubscribe: (() => void) | undefined
@@ -56,6 +57,7 @@ export async function initReactiveSettings(): Promise<void> {
5657
appColor = getSetting('appearance.appColor')
5758
sizeDisplay = getSetting('listing.sizeDisplay')
5859
sizeMismatchWarning = getSetting('listing.sizeMismatchWarning')
60+
stripedRows = getSetting('listing.stripedRows')
5961

6062
// Subscribe to changes (including cross-window changes)
6163
unsubscribe = onSettingChange((id, value) => {
@@ -91,6 +93,9 @@ export async function initReactiveSettings(): Promise<void> {
9193
case 'listing.sizeMismatchWarning':
9294
sizeMismatchWarning = value as boolean
9395
break
96+
case 'listing.stripedRows':
97+
stripedRows = value as boolean
98+
break
9499
}
95100
})
96101

@@ -149,6 +154,11 @@ export function getSizeMismatchWarning(): boolean {
149154
return sizeMismatchWarning
150155
}
151156

157+
/** Get whether striped rows are enabled */
158+
export function getStripedRows(): boolean {
159+
return stripedRows
160+
}
161+
152162
// ============================================================================
153163
// Formatting utilities that use reactive settings
154164
// ============================================================================

apps/desktop/src/lib/settings/sections/ListingSection.svelte

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
const dirSortDef = getSettingDefinition('listing.directorySortMode') ?? { label: '', description: '' }
1818
const sizeDisplayDef = getSettingDefinition('listing.sizeDisplay') ?? { label: '', description: '' }
1919
const sizeMismatchDef = getSettingDefinition('listing.sizeMismatchWarning') ?? { label: '', description: '' }
20+
const stripedRowsDef = getSettingDefinition('listing.stripedRows') ?? { label: '', description: '' }
2021
</script>
2122

2223
<SettingsSection title="Listing">
@@ -50,4 +51,14 @@
5051
<SettingSwitch id="listing.sizeMismatchWarning" />
5152
</SettingRow>
5253
{/if}
54+
{#if shouldShow('listing.stripedRows')}
55+
<SettingRow
56+
id="listing.stripedRows"
57+
label={stripedRowsDef.label}
58+
description={stripedRowsDef.description}
59+
{searchQuery}
60+
>
61+
<SettingSwitch id="listing.stripedRows" />
62+
</SettingRow>
63+
{/if}
5364
</SettingsSection>

apps/desktop/src/lib/settings/settings-registry.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,17 @@ export const settingsRegistry: SettingDefinition[] = [
157157
component: 'switch',
158158
},
159159

160+
{
161+
id: 'listing.stripedRows',
162+
section: ['General', 'Listing'],
163+
label: 'Striped rows',
164+
description: 'Alternate row shading for easier line tracking. Applies to both Full and Brief view modes.',
165+
keywords: ['stripe', 'zebra', 'alternate', 'row', 'shading', 'accessibility', 'a11y'],
166+
type: 'boolean',
167+
default: false,
168+
component: 'switch',
169+
},
170+
160171
// ========================================================================
161172
// General › File operations
162173
// ========================================================================

apps/desktop/src/lib/settings/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export interface SettingsValues {
9999
'listing.directorySortMode': DirectorySortMode
100100
'listing.sizeDisplay': SizeDisplayMode
101101
'listing.sizeMismatchWarning': boolean
102+
'listing.stripedRows': boolean
102103

103104
// File operations
104105
'fileOperations.mtpEnabled': boolean

0 commit comments

Comments
 (0)