Skip to content

Commit 6d68def

Browse files
committed
Settings: surface selection.recentSelections.maxCount in Behavior > Search (M9)
- Mirror `selection.recentSelections.maxCount` in `SearchSection.svelte` alongside the existing `search.recentSearches.maxCount` mirror; both are `showInAdvanced` entries browseable here - Add `'selection.recentSelections.maxCount': number` to `SettingsValues` in `types.ts` (latent gap: registry + applier already referenced it but the type interface was missing, causing svelte-check to fail) - Expand `SearchSection.svelte.test.ts` with three-row render assertion, selection-query filter test, and updated `getSetting` mock - Update `lib/settings/CLAUDE.md` section map to mention the new mirror
1 parent 8d5bd3d commit 6d68def

4 files changed

Lines changed: 50 additions & 17 deletions

File tree

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ Section ↔ component map (`sections/`):
117117
- `FileOperationsSection.svelte``Behavior > File operations` (extension changes only; `maxConflictsToShow` and
118118
`progressUpdateInterval` live in Advanced)
119119
- `DriveIndexingSection.svelte``Behavior > Drive indexing` (toggle + clear-index action)
120-
- `SearchSection.svelte``Behavior > Search` (auto-apply switch; mirrors the `search.recentSearches.maxCount` row from
121-
Advanced so users hunting under "search" find it)
120+
- `SearchSection.svelte``Behavior > Search` (auto-apply switch; mirrors the `search.recentSearches.maxCount` and
121+
`selection.recentSelections.maxCount` rows from Advanced so users hunting under "search" find them)
122122
- `AiSection.svelte` (+ `AiCloudSection.svelte`, `AiLocalSection.svelte`) → `AI`
123123
- `NetworkSection.svelte``File systems > SMB/Network shares`
124124
- `MtpSection.svelte``File systems > MTP (Android/Kindle/cameras)`

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

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
/**
33
* Settings > Behavior > Search.
44
*
5-
* Renders the auto-apply toggle (canonical home) plus a mirror of
6-
* `search.recentSearches.maxCount` so users who search "search" land on
7-
* something useful. The mirror follows the pattern in
8-
* `lib/settings/CLAUDE.md` § "Mirroring a setting in multiple sections":
9-
* the registry stays single-entry under Advanced, and rendering it a
10-
* second time here just adds a discoverability surface. Search-tree
5+
* Renders the auto-apply toggle (canonical home) plus mirrors of
6+
* `search.recentSearches.maxCount` and `selection.recentSelections.maxCount`
7+
* so users who browse "Behavior > Search" land on something useful. Both
8+
* mirrors follow the pattern in `lib/settings/CLAUDE.md` § "Mirroring a
9+
* setting in multiple sections": the registry entries stay in Advanced, and
10+
* rendering them here adds a discoverability surface. Search-tree
1111
* highlighting and full-text search remain canonical-only by design.
1212
*/
1313
import SettingsSection from '../components/SettingsSection.svelte'
@@ -28,9 +28,10 @@
2828
const defaultDef = { label: '', description: '' }
2929
const autoApplyDef = getSettingDefinition('search.autoApply') ?? defaultDef
3030
// Mirrored from Advanced: see `lib/settings/CLAUDE.md` § "Mirroring a setting in multiple sections".
31-
// The registry entry stays where it is; we just render the row here too so users hunting under
32-
// "search" find it. `shouldShow('search.recentSearches.maxCount')` still gates it under a query.
33-
const recentMaxDef = getSettingDefinition('search.recentSearches.maxCount') ?? defaultDef
31+
// The registry entries stay where they are; we just render the rows here too so users hunting
32+
// under "Search" find them. `shouldShow(...)` still gates them under a query.
33+
const recentSearchesMaxDef = getSettingDefinition('search.recentSearches.maxCount') ?? defaultDef
34+
const recentSelectionsMaxDef = getSettingDefinition('selection.recentSelections.maxCount') ?? defaultDef
3435
</script>
3536

3637
<SettingsSection title="Search">
@@ -48,12 +49,24 @@
4849
{#if shouldShow('search.recentSearches.maxCount')}
4950
<SettingRow
5051
id="search.recentSearches.maxCount"
51-
label={recentMaxDef.label}
52-
description={recentMaxDef.description}
52+
label={recentSearchesMaxDef.label}
53+
description={recentSearchesMaxDef.description}
5354
split
5455
{searchQuery}
5556
>
5657
<SettingNumberInput id="search.recentSearches.maxCount" />
5758
</SettingRow>
5859
{/if}
60+
61+
{#if shouldShow('selection.recentSelections.maxCount')}
62+
<SettingRow
63+
id="selection.recentSelections.maxCount"
64+
label={recentSelectionsMaxDef.label}
65+
description={recentSelectionsMaxDef.description}
66+
split
67+
{searchQuery}
68+
>
69+
<SettingNumberInput id="selection.recentSelections.maxCount" />
70+
</SettingRow>
71+
{/if}
5972
</SettingsSection>

apps/desktop/src/lib/settings/sections/SearchSection.svelte.test.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/**
22
* Tier-3 tests for `SearchSection.svelte`.
33
*
4-
* Pins the M6 contract:
4+
* Pins the M6 + M9 contract:
55
* - The auto-apply switch renders (canonical home).
66
* - The recent-searches max-count row renders here too (mirror; canonical is Advanced).
7+
* - The recent-selections max-count row renders here too (mirror; canonical is Advanced; added M9).
78
* - Rows respect the search filter (`shouldShow`).
89
* - No a11y violations.
910
*/
@@ -17,6 +18,7 @@ vi.mock('$lib/settings/settings-store', () => ({
1718
getSetting: vi.fn((key: string) => {
1819
if (key === 'search.autoApply') return true
1920
if (key === 'search.recentSearches.maxCount') return 1000
21+
if (key === 'selection.recentSelections.maxCount') return 1000
2022
return undefined
2123
}),
2224
setSetting: vi.fn(() => Promise.resolve()),
@@ -27,27 +29,42 @@ vi.mock('$lib/settings/settings-store', () => ({
2729
}))
2830

2931
describe('SearchSection', () => {
30-
it('renders both rows when no search filter is active', async () => {
32+
it('renders all three rows when no search filter is active', async () => {
3133
const target = document.createElement('div')
3234
document.body.appendChild(target)
3335
mount(SearchSection, { target, props: { searchQuery: '' } })
3436
await tick()
3537
const labels = Array.from(target.querySelectorAll('.setting-label')).map((el) => el.textContent.trim())
3638
expect(labels).toContain('Auto-apply searches')
3739
expect(labels).toContain('Recent searches to remember')
40+
expect(labels).toContain('Recent selections to remember')
3841
target.remove()
3942
})
4043

4144
it('filters rows by the active search query', async () => {
4245
const target = document.createElement('div')
4346
document.body.appendChild(target)
44-
// "debounce" is in the autoApply keywords but not in the recent-max definition, so only the
45-
// first row should render.
47+
// "debounce" is in the autoApply keywords but not in the recent-max definitions, so only
48+
// the first row should render.
4649
mount(SearchSection, { target, props: { searchQuery: 'debounce' } })
4750
await tick()
4851
const labels = Array.from(target.querySelectorAll('.setting-label')).map((el) => el.textContent.trim())
4952
expect(labels).toContain('Auto-apply searches')
5053
expect(labels).not.toContain('Recent searches to remember')
54+
expect(labels).not.toContain('Recent selections to remember')
55+
target.remove()
56+
})
57+
58+
it('hides the recent-selections row when a search filter is active (mirrors showInAdvanced behavior)', async () => {
59+
const target = document.createElement('div')
60+
document.body.appendChild(target)
61+
// `selection.recentSelections.maxCount` has `showInAdvanced: true`, so `buildSearchIndex`
62+
// excludes it. Any non-empty search query causes `shouldShow` to return false for it — the
63+
// mirror is only visible when browsing with no filter active.
64+
mount(SearchSection, { target, props: { searchQuery: 'selection' } })
65+
await tick()
66+
const labels = Array.from(target.querySelectorAll('.setting-label')).map((el) => el.textContent.trim())
67+
expect(labels).not.toContain('Recent selections to remember')
5168
target.remove()
5269
})
5370

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ export interface SettingsValues {
216216
// Search
217217
'search.autoApply': boolean
218218
'search.recentSearches.maxCount': number
219+
220+
// Selection
221+
'selection.recentSelections.maxCount': number
219222
}
220223

221224
export type SettingId = keyof SettingsValues

0 commit comments

Comments
 (0)