Skip to content

Commit 9493f88

Browse files
committed
Settings UI: Split horizontally 50-50%
Looks a lot cleaner now that it's uniformized
1 parent ce3eae1 commit 9493f88

11 files changed

Lines changed: 97 additions & 42 deletions

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ and action button styles), `SettingRow`, `SettingSwitch`, `SettingSelect`, `Sett
5858
`SettingRadioGroup`, `SettingToggleGroup`, `SettingsSidebar`, `SettingsContent`. Also `SectionSummary` for
5959
collapsed-section previews.
6060

61+
### 50-50 split layout guideline
62+
63+
`SettingRow` has a `split` prop that enforces a 50-50 grid layout (label left, control right). This keeps left edges of
64+
controls vertically aligned across rows for visual consistency. The settings window is resizable, so the split is
65+
percentage-based, not pixel-based.
66+
67+
**When to use `split`:** Setting rows where the control is a select, text input, password input, slider, number input,
68+
radio group, or combobox — anything that benefits from consistent horizontal alignment.
69+
70+
**When NOT to use `split`:**
71+
- Switches (too small; 50-50 wastes space and doesn't improve alignment)
72+
- Toggle groups (multi-button controls that may not fit in 50% width at narrow window sizes)
73+
- Full-width custom layouts (keyboard shortcuts table, license card, advanced auto-generated rows)
74+
75+
**When adding a new setting row**, decide if it should use `split` based on the rules above. If the control is a
76+
dropdown, text field, slider, or similar right-aligned input, add `split` to `<SettingRow>`. Description text below each
77+
row intentionally spans the full width.
78+
6179
### Other files
6280

6381
- **cloud-providers.ts** — Cloud provider preset definitions (OpenAI, Anthropic, Groq, etc.) and per-provider config

apps/desktop/src/lib/settings/components/SettingPasswordInput.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
display: flex;
138138
align-items: center;
139139
min-width: 180px;
140+
width: 100%;
140141
border: 1px solid var(--color-border);
141142
border-radius: var(--radius-sm);
142143
background: var(--color-bg-primary);

apps/desktop/src/lib/settings/components/SettingRow.svelte

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
disabled?: boolean
1313
disabledReason?: string
1414
requiresRestart?: boolean
15+
/** When true, label and control each take 50% width for consistent vertical alignment across rows. */
16+
split?: boolean
1517
searchQuery?: string
1618
children: Snippet
1719
descriptionContent?: Snippet
@@ -24,6 +26,7 @@
2426
disabled = false,
2527
disabledReason,
2628
requiresRestart = false,
29+
split = false,
2730
searchQuery = '',
2831
children,
2932
descriptionContent,
@@ -54,7 +57,7 @@
5457
</script>
5558

5659
<div class="setting-row" class:disabled>
57-
<div class="setting-header">
60+
<div class="setting-header" class:split>
5861
<div class="setting-label-wrapper">
5962
<label class="setting-label" for={id}
6063
>{#each labelSegments as segment, i (i)}{#if segment.matched}<mark class="search-highlight"
@@ -118,6 +121,13 @@
118121
gap: var(--spacing-md);
119122
}
120123
124+
.setting-header.split {
125+
display: grid;
126+
grid-template-columns: 1fr 1fr;
127+
align-items: center;
128+
gap: var(--spacing-md);
129+
}
130+
121131
.setting-label-wrapper {
122132
display: flex;
123133
align-items: center;
@@ -168,6 +178,12 @@
168178
flex-shrink: 0;
169179
}
170180
181+
.split .setting-control {
182+
/* Let controls stretch to fill the right column */
183+
min-width: 0;
184+
width: 100%;
185+
}
186+
171187
.setting-description {
172188
margin: var(--spacing-xs) 0 0;
173189
color: var(--color-text-secondary);

apps/desktop/src/lib/settings/components/SettingSelect.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
<style>
208208
.select-wrapper {
209209
min-width: 180px;
210+
width: 100%;
210211
}
211212
212213
.custom-input-wrapper {
@@ -257,7 +258,7 @@
257258
justify-content: space-between;
258259
gap: var(--spacing-sm);
259260
padding: var(--spacing-xs) var(--spacing-sm);
260-
min-width: 180px;
261+
width: 100%;
261262
border: 1px solid var(--color-border);
262263
border-radius: var(--radius-sm);
263264
background: var(--color-bg-primary);

apps/desktop/src/lib/settings/components/SettingSlider.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@
115115
display: flex;
116116
align-items: center;
117117
gap: var(--spacing-sm);
118-
min-width: 280px;
118+
min-width: 0;
119+
width: 100%;
119120
}
120121
121122
/* The root needs explicit sizing for Ark UI slider to work */

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@
433433
try {
434434
await startAiDownload()
435435
} catch (e) {
436+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- set by handleCancelDownload() during the await
436437
if (downloadCancelledByUser) {
437438
logger.info('AI download cancelled by user')
438439
} else {
@@ -716,6 +717,7 @@
716717
id="ai.cloudProvider"
717718
label="Service"
718719
description="Which cloud AI service to use."
720+
split
719721
{searchQuery}
720722
>
721723
<select
@@ -741,6 +743,7 @@
741743
id="ai.cloudProviderConfigs"
742744
label="Endpoint"
743745
description="API endpoint URL for the selected service."
746+
split
744747
{searchQuery}
745748
>
746749
{#if showEditableBaseUrl}
@@ -775,6 +778,7 @@
775778
id="ai.cloudProviderConfigs"
776779
label="API key"
777780
description="Your API key for this service."
781+
split
778782
{searchQuery}
779783
>
780784
<SettingPasswordInput
@@ -794,6 +798,7 @@
794798
id="ai.cloudProviderConfigs"
795799
label="Model"
796800
description="The model name to use for completions."
801+
split
797802
{searchQuery}
798803
>
799804
{#if availableModels.length > 0}
@@ -980,6 +985,7 @@
980985
id="ai.localContextSize"
981986
label="Context window"
982987
description="Number of tokens the local model can process at once. Larger values use more memory."
988+
split
983989
{searchQuery}
984990
>
985991
<div class="context-size-controls">
@@ -1200,7 +1206,7 @@
12001206
12011207
/* Cloud provider select */
12021208
.cloud-provider-select {
1203-
min-width: 180px;
1209+
width: 100%;
12041210
padding: var(--spacing-sm) var(--spacing-md);
12051211
border: 1px solid var(--color-border);
12061212
border-radius: var(--radius-sm);
@@ -1227,7 +1233,7 @@
12271233
12281234
/* Text input (same style as other setting inputs) */
12291235
.text-input {
1230-
min-width: 180px;
1236+
width: 100%;
12311237
padding: var(--spacing-sm) var(--spacing-md);
12321238
border: 1px solid var(--color-border);
12331239
border-radius: var(--radius-sm);
@@ -1491,11 +1497,12 @@
14911497
/* Model combobox */
14921498
.combobox-wrapper {
14931499
position: relative;
1500+
width: 100%;
14941501
}
14951502
14961503
.combobox-input-wrapper {
14971504
position: relative;
1498-
display: inline-flex;
1505+
display: flex;
14991506
align-items: center;
15001507
}
15011508

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
<SettingsSection title="Appearance">
6666
{#if shouldShow('appearance.appColor')}
67-
<SettingRow id="appearance.appColor" label={appColorDef.label} description="" {searchQuery}>
67+
<SettingRow id="appearance.appColor" label={appColorDef.label} description="" split {searchQuery}>
6868
{#snippet descriptionContent()}
6969
To change your system theme color, go to
7070
<button type="button" class="appearance-link" onclick={() => void openAppearanceSettings()}
@@ -131,6 +131,7 @@
131131
id="appearance.fileSizeFormat"
132132
label={fileSizeDef.label}
133133
description={fileSizeDef.description}
134+
split
134135
{searchQuery}
135136
>
136137
<SettingSelect id="appearance.fileSizeFormat" />
@@ -142,6 +143,7 @@
142143
id="appearance.dateTimeFormat"
143144
label={dateTimeDef.label}
144145
description={dateTimeDef.description}
146+
split
145147
{searchQuery}
146148
>
147149
<div class="date-time-setting">
@@ -265,8 +267,9 @@
265267
}
266268
267269
.date-time-setting {
268-
/* Fixed width to prevent layout shift when custom content appears */
269-
width: 250px;
270+
/* Fill the split column; min-width prevents collapse */
271+
width: 100%;
272+
min-width: 200px;
270273
}
271274
272275
.custom-format {

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

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -78,30 +78,28 @@
7878
{/if}
7979

8080
<div class="index-info">
81-
<div class="info-row">
81+
<div class="index-row">
8282
<span class="info-label">Index size</span>
83-
<span class="info-value">
84-
{#if dbFileSize != null}
85-
{formatFileSize(dbFileSize)}
86-
{:else}
87-
No index
88-
{/if}
89-
</span>
83+
<div class="index-controls">
84+
<Button
85+
variant="secondary"
86+
size="mini"
87+
onclick={handleClearIndex}
88+
disabled={clearing || dbFileSize == null}
89+
>
90+
{clearing ? 'Clearing...' : 'Clear index'}
91+
</Button>
92+
<span class="info-value">
93+
{#if dbFileSize != null}
94+
{formatFileSize(dbFileSize)}
95+
{:else}
96+
No index
97+
{/if}
98+
</span>
99+
</div>
90100
</div>
91101

92-
<div class="clear-action">
93-
<Button
94-
variant="secondary"
95-
size="mini"
96-
onclick={handleClearIndex}
97-
disabled={clearing || dbFileSize == null}
98-
>
99-
{clearing ? 'Clearing...' : 'Clear index'}
100-
</Button>
101-
<span class="clear-description">
102-
Deletes the index database. A fresh scan starts next time indexing is enabled.
103-
</span>
104-
</div>
102+
<p class="clear-description">Deletes the index database. A fresh scan starts next time indexing is enabled.</p>
105103

106104
{#if clearError}
107105
<div class="clear-error">{clearError}</div>
@@ -112,36 +110,39 @@
112110
<style>
113111
.index-info {
114112
padding: var(--spacing-sm) 0;
113+
border-bottom: 1px solid var(--color-border-subtle);
115114
}
116115
117-
.info-row {
118-
display: flex;
116+
.index-row {
117+
display: grid;
118+
grid-template-columns: 1fr 1fr;
119119
align-items: center;
120-
justify-content: space-between;
121-
padding: var(--spacing-xs) 0;
120+
gap: var(--spacing-md);
122121
}
123122
124123
.info-label {
125124
font-weight: 500;
126125
color: var(--color-text-primary);
127126
}
128127
128+
.index-controls {
129+
display: flex;
130+
align-items: center;
131+
justify-content: space-between;
132+
gap: var(--spacing-sm);
133+
}
134+
129135
.info-value {
130136
color: var(--color-text-secondary);
131137
font-family: var(--font-mono);
132138
font-size: var(--font-size-sm);
133139
}
134140
135-
.clear-action {
136-
display: flex;
137-
align-items: center;
138-
gap: var(--spacing-sm);
139-
margin-top: var(--spacing-sm);
140-
}
141-
142141
.clear-description {
143-
color: var(--color-text-tertiary);
142+
margin: var(--spacing-xs) 0 0;
143+
color: var(--color-text-secondary);
144144
font-size: var(--font-size-sm);
145+
line-height: 1.4;
145146
}
146147
147148
.clear-error {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
id="fileOperations.allowFileExtensionChanges"
2828
label={extensionChangesDef.label}
2929
description={extensionChangesDef.description}
30+
split
3031
{searchQuery}
3132
>
3233
<SettingRadioGroup id="fileOperations.allowFileExtensionChanges" />
@@ -38,6 +39,7 @@
3839
id="fileOperations.progressUpdateInterval"
3940
label={progressIntervalDef.label}
4041
description={progressIntervalDef.description}
42+
split
4143
{searchQuery}
4244
>
4345
<SettingSlider id="fileOperations.progressUpdateInterval" unit="ms" />
@@ -49,6 +51,7 @@
4951
id="fileOperations.maxConflictsToShow"
5052
label={maxConflictsDef.label}
5153
description={maxConflictsDef.description}
54+
split
5255
{searchQuery}
5356
>
5457
<SettingSelect id="fileOperations.maxConflictsToShow" />

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
description={mcpPortDef.description}
7373
disabled={!mcpEnabled}
7474
requiresRestart={mcpPortDef.requiresRestart}
75+
split
7576
{searchQuery}
7677
>
7778
<div class="port-setting">

0 commit comments

Comments
 (0)