Skip to content

Commit 35a4239

Browse files
committed
Refactor: Deduplicate Settings CSS
- Now using a SettingsSection parent component that owns .section, .section-title, and .section-action-btn styles for all 11 sections - Extract createShouldShow() helper, replacing identical shouldShow boilerplate in 9 sections - Unify 5 button class names into section-action-btn
1 parent 4d07ad0 commit 35a4239

15 files changed

Lines changed: 118 additions & 344 deletions

apps/desktop/coverage-allowlist.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"settings/components/SettingSwitch.svelte": { "reason": "UI component, logic is simple" },
8282
"settings/components/SettingToggleGroup.svelte": { "reason": "UI component, logic is simple" },
8383
"settings/components/SettingsContent.svelte": { "reason": "UI layout component" },
84+
"settings/components/SettingsSection.svelte": { "reason": "UI layout component, no logic" },
8485
"settings/components/SettingsSidebar.svelte": { "reason": "UI component, search logic tested via registry" },
8586
"settings/sections/AdvancedSection.svelte": { "reason": "UI section, depends on Tauri APIs" },
8687
"settings/sections/AppearanceSection.svelte": { "reason": "UI section, simple rendering" },
@@ -94,6 +95,7 @@
9495
"settings/sections/NetworkSection.svelte": { "reason": "UI section, simple rendering" },
9596
"settings/sections/ThemesSection.svelte": { "reason": "UI section, simple rendering" },
9697
"settings/sections/DriveIndexingSection.svelte": { "reason": "UI section, depends on Tauri invoke" },
98+
"settings/sections/ListingSection.svelte": { "reason": "UI section, simple rendering" },
9799
"settings/sections/UpdatesSection.svelte": { "reason": "UI section, simple rendering" },
98100
"settings/settings-store.ts": { "reason": "Depends on Tauri store APIs" },
99101
"settings/settings-window.ts": { "reason": "Depends on Tauri window APIs" },

apps/desktop/src/lib/settings/CLAUDE.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ Single source of truth for all settings. Each `SettingDefinition` contains:
3939

4040
### Components (`components/`)
4141

42-
9 reusable setting UI primitives used by section components: `SettingRow`, `SettingSwitch`, `SettingSelect`,
43-
`SettingSlider`, `SettingNumberInput`, `SettingRadioGroup`, `SettingToggleGroup`, `SettingsSidebar`, `SettingsContent`.
44-
Also `SectionSummary` for collapsed-section previews.
42+
10 reusable setting UI primitives used by section components: `SettingsSection` (wrapper providing shared section title
43+
and action button styles), `SettingRow`, `SettingSwitch`, `SettingSelect`, `SettingSlider`, `SettingNumberInput`,
44+
`SettingRadioGroup`, `SettingToggleGroup`, `SettingsSidebar`, `SettingsContent`. Also `SectionSummary` for
45+
collapsed-section previews.
4546

4647
### Other files
4748

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script lang="ts">
2+
import type { Snippet } from 'svelte'
3+
4+
interface Props {
5+
title: string
6+
children: Snippet
7+
}
8+
9+
const { title, children }: Props = $props()
10+
</script>
11+
12+
<div class="section">
13+
<h2 class="section-title">{title}</h2>
14+
{@render children()}
15+
</div>
16+
17+
<style>
18+
.section {
19+
margin-bottom: var(--spacing-lg);
20+
}
21+
22+
.section-title {
23+
font-size: var(--font-size-lg);
24+
font-weight: 600;
25+
color: var(--color-text-primary);
26+
margin: 0 0 var(--spacing-sm);
27+
padding-bottom: var(--spacing-xs);
28+
border-bottom: 1px solid var(--color-border);
29+
}
30+
31+
.section :global(.section-action-btn) {
32+
padding: var(--spacing-xs) var(--spacing-sm);
33+
border: 1px solid var(--color-border);
34+
border-radius: var(--radius-sm);
35+
background: var(--color-bg-secondary);
36+
color: var(--color-text-secondary);
37+
font-size: var(--font-size-sm);
38+
cursor: default;
39+
}
40+
41+
.section :global(.section-action-btn:disabled) {
42+
opacity: 0.5;
43+
cursor: not-allowed;
44+
}
45+
</style>

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

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
type SettingsValues,
1111
formatDuration,
1212
} from '$lib/settings'
13+
import SettingsSection from '../components/SettingsSection.svelte'
1314
import { Switch } from '@ark-ui/svelte/switch'
1415
import { NumberInput, type NumberInputValueChangeDetails } from '@ark-ui/svelte/number-input'
1516
import { searchAdvancedSettings, getMatchIndicesForLabel, highlightMatches } from '$lib/settings/settings-search'
@@ -79,9 +80,7 @@
7980
}
8081
</script>
8182

82-
<div class="section">
83-
<h2 class="section-title">Advanced</h2>
84-
83+
<SettingsSection title="Advanced">
8584
<div class="warning-banner">
8685
<span class="warning-icon">⚠️</span>
8786
<span>
@@ -90,7 +89,7 @@
9089
</div>
9190

9291
<div class="header-actions">
93-
<button class="reset-all-btn" onclick={handleResetAll}>Reset all to defaults</button>
92+
<button class="section-action-btn" onclick={handleResetAll}>Reset all to defaults</button>
9493
</div>
9594

9695
<div class="advanced-settings">
@@ -164,22 +163,9 @@
164163
</div>
165164
{/each}
166165
</div>
167-
</div>
166+
</SettingsSection>
168167

169168
<style>
170-
.section {
171-
margin-bottom: var(--spacing-lg);
172-
}
173-
174-
.section-title {
175-
font-size: var(--font-size-lg);
176-
font-weight: 600;
177-
color: var(--color-text-primary);
178-
margin: 0 0 var(--spacing-sm);
179-
padding-bottom: var(--spacing-xs);
180-
border-bottom: 1px solid var(--color-border);
181-
}
182-
183169
.warning-banner {
184170
display: flex;
185171
align-items: flex-start;
@@ -203,16 +189,6 @@
203189
margin-bottom: var(--spacing-lg);
204190
}
205191
206-
.reset-all-btn {
207-
padding: var(--spacing-xs) var(--spacing-sm);
208-
border: 1px solid var(--color-border);
209-
border-radius: var(--radius-sm);
210-
background: var(--color-bg-secondary);
211-
color: var(--color-text-secondary);
212-
font-size: var(--font-size-sm);
213-
cursor: default;
214-
}
215-
216192
.advanced-settings {
217193
display: flex;
218194
flex-direction: column;

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

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
11
<script lang="ts">
2+
import SettingsSection from '../components/SettingsSection.svelte'
23
import SettingRow from '../components/SettingRow.svelte'
34
import SettingSwitch from '../components/SettingSwitch.svelte'
45
import SettingSelect from '../components/SettingSelect.svelte'
56
import SettingToggleGroup from '../components/SettingToggleGroup.svelte'
67
import SettingRadioGroup from '../components/SettingRadioGroup.svelte'
78
import { getSettingDefinition, getSetting, setSetting } from '$lib/settings'
8-
import { getMatchingSettingIds } from '$lib/settings/settings-search'
9+
import { createShouldShow } from '$lib/settings/settings-search'
910
1011
interface Props {
1112
searchQuery: string
1213
}
1314
1415
const { searchQuery }: Props = $props()
1516
16-
// Get matching setting IDs for filtering
17-
const matchingIds = $derived(searchQuery.trim() ? getMatchingSettingIds(searchQuery) : null)
18-
19-
// Check if a setting should be shown
20-
function shouldShow(id: string): boolean {
21-
if (!matchingIds) return true
22-
return matchingIds.has(id)
23-
}
17+
const shouldShow = $derived(createShouldShow(searchQuery))
2418
2519
// Get definitions for rendering (with fallbacks for type safety)
2620
const uiDensityDef = getSettingDefinition('appearance.uiDensity') ?? { label: '', description: '' }
@@ -55,9 +49,7 @@
5549
}
5650
</script>
5751

58-
<div class="section">
59-
<h2 class="section-title">Appearance</h2>
60-
52+
<SettingsSection title="Appearance">
6153
{#if shouldShow('appearance.uiDensity')}
6254
<SettingRow
6355
id="appearance.uiDensity"
@@ -140,22 +132,9 @@
140132
</div>
141133
</SettingRow>
142134
{/if}
143-
</div>
135+
</SettingsSection>
144136

145137
<style>
146-
.section {
147-
margin-bottom: var(--spacing-lg);
148-
}
149-
150-
.section-title {
151-
font-size: var(--font-size-lg);
152-
font-weight: 600;
153-
color: var(--color-text-primary);
154-
margin: 0 0 var(--spacing-sm);
155-
padding-bottom: var(--spacing-xs);
156-
border-bottom: 1px solid var(--color-border);
157-
}
158-
159138
.date-time-setting {
160139
/* Fixed width to prevent layout shift when custom content appears */
161140
width: 250px;

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

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
<script lang="ts">
22
import { invoke } from '@tauri-apps/api/core'
33
import { onMount } from 'svelte'
4+
import SettingsSection from '../components/SettingsSection.svelte'
45
import SettingRow from '../components/SettingRow.svelte'
56
import SettingSwitch from '../components/SettingSwitch.svelte'
67
import { getSettingDefinition } from '$lib/settings'
78
import { formatFileSize } from '$lib/settings/reactive-settings.svelte'
8-
import { getMatchingSettingIds } from '$lib/settings/settings-search'
9+
import { createShouldShow } from '$lib/settings/settings-search'
910
import { getAppLogger } from '$lib/logging/logger'
1011
1112
interface Props {
@@ -16,13 +17,7 @@
1617
1718
const log = getAppLogger('settings')
1819
19-
// Get matching setting IDs for filtering
20-
const matchingIds = $derived(searchQuery.trim() ? getMatchingSettingIds(searchQuery) : null)
21-
22-
function shouldShow(id: string): boolean {
23-
if (!matchingIds) return true
24-
return matchingIds.has(id)
25-
}
20+
const shouldShow = $derived(createShouldShow(searchQuery))
2621
2722
const enabledDef = getSettingDefinition('indexing.enabled') ?? { label: '', description: '' }
2823
@@ -74,9 +69,7 @@
7469
})
7570
</script>
7671

77-
<div class="section">
78-
<h2 class="section-title">Drive indexing</h2>
79-
72+
<SettingsSection title="Drive indexing">
8073
{#if shouldShow('indexing.enabled')}
8174
<SettingRow id="indexing.enabled" label={enabledDef.label} description={enabledDef.description} {searchQuery}>
8275
<SettingSwitch id="indexing.enabled" />
@@ -96,7 +89,7 @@
9689
</div>
9790

9891
<div class="clear-action">
99-
<button class="clear-btn" onclick={handleClearIndex} disabled={clearing || dbFileSize == null}>
92+
<button class="section-action-btn" onclick={handleClearIndex} disabled={clearing || dbFileSize == null}>
10093
{clearing ? 'Clearing...' : 'Clear index'}
10194
</button>
10295
<span class="clear-description">
@@ -108,22 +101,9 @@
108101
<div class="clear-error">{clearError}</div>
109102
{/if}
110103
</div>
111-
</div>
104+
</SettingsSection>
112105

113106
<style>
114-
.section {
115-
margin-bottom: var(--spacing-lg);
116-
}
117-
118-
.section-title {
119-
font-size: var(--font-size-lg);
120-
font-weight: 600;
121-
color: var(--color-text-primary);
122-
margin: 0 0 var(--spacing-sm);
123-
padding-bottom: var(--spacing-xs);
124-
border-bottom: 1px solid var(--color-border);
125-
}
126-
127107
.index-info {
128108
padding: var(--spacing-sm) 0;
129109
}
@@ -153,22 +133,6 @@
153133
margin-top: var(--spacing-sm);
154134
}
155135
156-
.clear-btn {
157-
padding: var(--spacing-xs) var(--spacing-sm);
158-
border: 1px solid var(--color-border);
159-
border-radius: var(--radius-sm);
160-
background: var(--color-bg-secondary);
161-
color: var(--color-text-secondary);
162-
font-size: var(--font-size-sm);
163-
cursor: default;
164-
white-space: nowrap;
165-
}
166-
167-
.clear-btn:disabled {
168-
opacity: 0.5;
169-
cursor: not-allowed;
170-
}
171-
172136
.clear-description {
173137
color: var(--color-text-tertiary);
174138
font-size: var(--font-size-sm);

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

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,20 @@
11
<script lang="ts">
2+
import SettingsSection from '../components/SettingsSection.svelte'
23
import SettingRow from '../components/SettingRow.svelte'
34
import SettingSwitch from '../components/SettingSwitch.svelte'
45
import SettingSelect from '../components/SettingSelect.svelte'
56
import SettingSlider from '../components/SettingSlider.svelte'
67
import SettingRadioGroup from '../components/SettingRadioGroup.svelte'
78
import { getSettingDefinition } from '$lib/settings'
8-
import { getMatchingSettingIds } from '$lib/settings/settings-search'
9+
import { createShouldShow } from '$lib/settings/settings-search'
910
1011
interface Props {
1112
searchQuery: string
1213
}
1314
1415
const { searchQuery }: Props = $props()
1516
16-
// Get matching setting IDs for filtering
17-
const matchingIds = $derived(searchQuery.trim() ? getMatchingSettingIds(searchQuery) : null)
18-
19-
// Check if a setting should be shown
20-
function shouldShow(id: string): boolean {
21-
if (!matchingIds) return true
22-
return matchingIds.has(id)
23-
}
17+
const shouldShow = $derived(createShouldShow(searchQuery))
2418
2519
const defaultDef = { label: '', description: '', disabled: false, disabledReason: '' }
2620
const confirmDeleteDef = getSettingDefinition('fileOperations.confirmBeforeDelete') ?? defaultDef
@@ -30,9 +24,7 @@
3024
const maxConflictsDef = getSettingDefinition('fileOperations.maxConflictsToShow') ?? defaultDef
3125
</script>
3226

33-
<div class="section">
34-
<h2 class="section-title">File operations</h2>
35-
27+
<SettingsSection title="File operations">
3628
{#if shouldShow('fileOperations.confirmBeforeDelete')}
3729
<SettingRow
3830
id="fileOperations.confirmBeforeDelete"
@@ -91,19 +83,4 @@
9183
<SettingSelect id="fileOperations.maxConflictsToShow" />
9284
</SettingRow>
9385
{/if}
94-
</div>
95-
96-
<style>
97-
.section {
98-
margin-bottom: var(--spacing-lg);
99-
}
100-
101-
.section-title {
102-
font-size: var(--font-size-lg);
103-
font-weight: 600;
104-
color: var(--color-text-primary);
105-
margin: 0 0 var(--spacing-sm);
106-
padding-bottom: var(--spacing-xs);
107-
border-bottom: 1px solid var(--color-border);
108-
}
109-
</style>
86+
</SettingsSection>

0 commit comments

Comments
 (0)