Model visibility dialog hide-all preservation bug fixes
Cluster 43485. Data as of
Nothing open on GitHub. Fix merged in #43496 on 11 Jun 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #43496. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #43496. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These issues and pull requests concern a bug in the desktop model visibility dialog where toggling off the last model in a provider group incorrectly re-enables all models, along with multiple fix attempts to preserve explicit hide-all states for providers.
Open issues0
Open PRs0
Fixes on record
-
#43496 fix(desktop): preserve explicit hide-all choice in model visibility dialog
files:
apps/desktop/src/components/model-visibility-dialog.tsx,apps/desktop/src/store/model-visibility.test.ts,apps/desktop/src/store/model-visibility.tsShow the diff
-
apps/desktop/src/components/model-visibility-dialog.tsx@@ -14,6 +14,8 @@ import { $visibleModels, collapseModelFamilies, effectiveVisibleKeys, + emptyProviderSentinelKey, + isProviderSentinel, modelVisibilityKey, setVisibleModels } from '@/store/model-visibility' @@ -61,10 +63,21 @@ export function ModelVisibilityDialog({ const toggle = (provider: ModelOptionProvider, model: string) => { const next = new Set(effectiveVisibleKeys($visibleModels.get(), providers)) const key = modelVisibilityKey(provider.slug, model) + const sentinel = emptyProviderSentinelKey(provider.slug) if (next.has(key)) { next.delete(key) + + // Check if this was the last real model for this provider. + const remainingForProvider = [...next].some( + k => k.startsWith(`${provider.slug}::`) && !isProviderSentinel(k) + ) + + if (!remainingForProvider) { + next.add(sentinel) + } } else { + next.delete(sentinel) next.add(key) } -
apps/desktop/src/store/model-visibility.test.ts@@ -2,7 +2,12 @@ import { describe, expect, it } from 'vitest' import type { ModelOptionProvider } from '@/types/hermes' -import { effectiveVisibleKeys, modelVisibilityKey } from './model-visibility' +import { + effectiveVisibleKeys, + emptyProviderSentinelKey, + isProviderSentinel, + modelVisibilityKey +} from './model-visibility' const provider = (slug: string, models: string[]): ModelOptionProvider => ({ models, @@ -34,4 +39,48 @@ describe('model visibility', () => { expect(visible.has(modelVisibilityKey('local-ollama', 'qwen3:latest'))).toBe(true) expect(visible.has(modelVisibilityKey('local-ollama', 'llama3.2:latest'))).toBe(false) }) + + it('preserves hidden-provider sentinel without re-adding defaults', () => { + // User explicitly hid all models for "nous" — sentinel marks this choice. + const stored = new Set([emptyProviderSentinelKey('nous')]) + + const visible = effectiveVisibleKeys(stored, [ + provider('nous', ['hermes-3-llama-3.1-70b', 'hermes-3-llama-3.1-8b']), + provider('ollama', ['qwen3:latest']) + ]) + + expect(visible.has(modelVisibilityKey('nous', 'hermes-3-llama-3.1-70b'))).toBe(false) + expect(visible.has(modelVisibilityKey('nous', 'hermes-3-llama-3.1-8b'))).toBe(false) + // Sentinel itself is stripped from the result. + expect(visible.has(emptyProviderSentinelKey('nous'))).toBe(false) + // Other providers still get defaults. + expect(visible.has(modelVisibilityKey('ollama', 'qwen3:latest'))).toBe(true) + }) + + it('restores model when toggling on after hiding all', () => { + // Simulates: user hid all "nous" models, then toggles one back on. + const stored = new Set([ + emptyProviderSentinelKey('nous'), + modelVisibilityKey('ollama', 'qwen3:latest') + ]) + + // After toggle: sentinel removed, one model added. + const afterToggle = new Set(stored) + afterToggle.delete(emptyProviderSentinelKey('nous')) + afterToggle.add(modelVisibilityKey('nous', 'hermes-3-llama-3.1-70b')) + + const visible = effectiveVisibleKeys(afterToggle, [ + provider('nous', ['hermes-3-llama-3.1-70b', 'hermes-3-llama-3.1-8b']), + provider('ollama', ['qwen3:latest']) + ]) + + expect(visible.has(modelVisibilityKey('nous', 'hermes-3-llama-3.1-70b'))).toBe(true) + expect(visible.has(modelVisibilityKey('nous', 'hermes-3-llama-3.1-8b'))).toBe(false) + }) + + it('sentinel key helper produces correct format', () => { + expect(emptyProviderSentinelKey('openai')).toBe('openai::') + expect(isProviderSentinel('openai::')).toBe(true) + expect(isProviderSentinel('openai::gpt-4o')).toBe(false) + }) }) -
apps/desktop/src/store/model-visibility.ts@@ -13,6 +13,19 @@ export const DEFAULT_VISIBLE_PER_PROVIDER = 50 * that contain a single colon, e.g. `model:tag`). */ export const modelVisibilityKey = (provider: string, model: string): string => `${provider}::${model}` +/** Sentinel key suffix stored when the user explicitly hides ALL models for a + * provider. Distinguishes "user hid everything" from "never customized" so + * `effectiveVisibleKeys` does not re-add defaults for that provider. */ +export const EMPTY_PROVIDER_SENTINEL = '' + +/** Build the sentinel key for a provider whose last model was toggled off. */ +export const emptyProviderSentinelKey = (provider: string): string => + modelVisibilityKey(provider, EMPTY_PROVIDER_SENTINEL) + +/** Check whether a stored key is a provider-hidden sentinel. */ +export const isProviderSentinel = (key: string): boolean => + key.endsWith('::') + /** A model and its optional `…-fast` sibling, collapsed into one logical row. * `id` is the canonical (base) model; `fastId` is the fast variant if present. */ export interface ModelFamily { @@ -116,9 +129,12 @@ export function effectiveVisibleKeys( for (const provider of providers) { const providerPrefix = `${provider.slug}::` - const hasStoredProvider = [...stored].some(key => key.startsWith(providerPrefix)) + const hasStoredProvider = [...stored].some( + key => key.startsWith(providerPrefix) && !isProviderSentinel(key) + ) + const hasSentinel = stored.has(emptyProviderSentinelKey(provider.slug)) - if (hasStoredProvider) { + if (hasStoredProvider || hasSentinel) { continue } @@ -129,5 +145,12 @@ export function effectiveVisibleKeys( } } + // Strip sentinel keys — they are bookkeeping, not real visibility entries. + for (const key of [...next]) { + if (isProviderSentinel(key)) { + next.delete(key) + } + } + return next }
-
1 PR closed without merging
From the close comments: superseded by another PR 1
-
#43542 fix(desktop): preserve empty model visibility providers · superseded by another PR →
#43496,#43485
Closed issues · 1
- Closed#43485[Bug] Model visibility: toggling off last model in a provider group re-enables all models
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #43496
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 11 Jun 2026