Skip to content

Commit 95636bd

Browse files
authored
feat(desktop): collapse model provider sections (anomalyco#39283)
1 parent 7dae9a1 commit 95636bd

2 files changed

Lines changed: 139 additions & 36 deletions

File tree

packages/app/src/components/settings-v2/models.tsx

Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
55
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
66
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
77
import { type Component, For, Show } from "solid-js"
8+
import { createStore } from "solid-js/store"
89
import { useLanguage } from "@/context/language"
910
import { useModels } from "@/context/models"
11+
import { useServerSDK } from "@/context/server-sdk"
1012
import { popularProviders } from "@/hooks/use-providers"
13+
import { Persist, persisted } from "@/utils/persist"
1114
import { SettingsListV2 } from "./parts/list"
1215
import { SettingsRowV2 } from "./parts/row"
1316
import "./settings-v2.css"
@@ -19,6 +22,11 @@ const PROVIDER_ICON_SIZE = 16
1922
export const SettingsModelsV2: Component = () => {
2023
const language = useLanguage()
2124
const models = useModels()
25+
const serverSdk = useServerSDK()
26+
const [store, setStore] = persisted(
27+
Persist.serverGlobal(serverSdk().scope, "settings-v2.models.providers"),
28+
createStore({ collapsed: {} as Record<string, boolean> }),
29+
)
2230

2331
const list = useFilteredList<ModelItem>({
2432
items: (_filter) => models.list(),
@@ -94,41 +102,82 @@ export const SettingsModelsV2: Component = () => {
94102
}
95103
>
96104
<For each={list.grouped.latest}>
97-
{(group) => (
98-
<div class="settings-v2-section" data-component="settings-models-provider">
99-
<div class="settings-v2-models-group-header">
100-
<ProviderIcon
101-
id={group.category}
102-
width={PROVIDER_ICON_SIZE}
103-
height={PROVIDER_ICON_SIZE}
104-
class="settings-v2-models-provider-icon shrink-0"
105-
/>
106-
<h3 class="settings-v2-section-title">{group.items[0].provider.name}</h3>
105+
{(group) => {
106+
const searching = () => list.filter().length > 0
107+
const expanded = () => searching() || !store.collapsed[group.category]
108+
109+
return (
110+
<div
111+
class="settings-v2-section"
112+
data-component="settings-models-provider"
113+
data-expanded={expanded() ? "" : undefined}
114+
>
115+
<h3 class="settings-v2-models-group-header">
116+
<button
117+
type="button"
118+
class="settings-v2-models-group-trigger"
119+
aria-expanded={expanded()}
120+
disabled={searching()}
121+
onClick={() => setStore("collapsed", group.category, expanded())}
122+
>
123+
<span class="settings-v2-models-group-chevron">
124+
<Show
125+
when={expanded()}
126+
fallback={
127+
<svg width="5" height="6" viewBox="0 0 5 6" fill="none" aria-hidden="true">
128+
<path
129+
d="M0.75194 5.31663C0.41861 5.51103 0 5.27063 0 4.88473V0.500754C0 0.114854 0.41861 -0.125577 0.75194 0.0688635L4.5096 2.26084C4.8404 2.45378 4.8404 2.93168 4.5096 3.12462L0.75194 5.31663Z"
130+
fill="currentColor"
131+
/>
132+
</svg>
133+
}
134+
>
135+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
136+
<path
137+
d="M5.37624 6.75194C5.18184 6.41861 5.42224 6 5.80814 6H10.1921C10.578 6 10.8184 6.41861 10.624 6.75194L8.43203 10.5096C8.23909 10.8404 7.76119 10.8404 7.56825 10.5096L5.37624 6.75194Z"
138+
fill="currentColor"
139+
/>
140+
</svg>
141+
</Show>
142+
</span>
143+
<span class="settings-v2-models-group-label">
144+
<ProviderIcon
145+
id={group.category}
146+
width={PROVIDER_ICON_SIZE}
147+
height={PROVIDER_ICON_SIZE}
148+
class="settings-v2-models-provider-icon shrink-0"
149+
/>
150+
<span class="settings-v2-section-title">{group.items[0].provider.name}</span>
151+
</span>
152+
</button>
153+
</h3>
154+
<Show when={expanded()}>
155+
<SettingsListV2>
156+
<For each={group.items}>
157+
{(item) => {
158+
const key = { providerID: item.provider.id, modelID: item.id }
159+
return (
160+
<SettingsRowV2 title={item.name} description="">
161+
<div>
162+
<Switch
163+
checked={models.visible(key)}
164+
onChange={(checked) => {
165+
models.setVisibility(key, checked)
166+
}}
167+
hideLabel
168+
>
169+
{item.name}
170+
</Switch>
171+
</div>
172+
</SettingsRowV2>
173+
)
174+
}}
175+
</For>
176+
</SettingsListV2>
177+
</Show>
107178
</div>
108-
<SettingsListV2>
109-
<For each={group.items}>
110-
{(item) => {
111-
const key = { providerID: item.provider.id, modelID: item.id }
112-
return (
113-
<SettingsRowV2 title={item.name} description="">
114-
<div>
115-
<Switch
116-
checked={models.visible(key)}
117-
onChange={(checked) => {
118-
models.setVisibility(key, checked)
119-
}}
120-
hideLabel
121-
>
122-
{item.name}
123-
</Switch>
124-
</div>
125-
</SettingsRowV2>
126-
)
127-
}}
128-
</For>
129-
</SettingsListV2>
130-
</div>
131-
)}
179+
)
180+
}}
132181
</For>
133182
</Show>
134183
</Show>

packages/app/src/components/settings-v2/settings-v2.css

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,14 +373,64 @@
373373
}
374374

375375
.settings-v2-models {
376-
gap: 24px;
376+
gap: 12px;
377+
}
378+
379+
.settings-v2-models .settings-v2-section {
380+
gap: 12px;
381+
}
382+
383+
.settings-v2-models .settings-v2-section[data-expanded] {
384+
padding-bottom: 12px;
377385
}
378386

379387
.settings-v2-models-group-header {
388+
display: flex;
389+
align-items: center;
390+
height: 28px;
391+
}
392+
393+
.settings-v2-models-group-trigger {
394+
display: flex;
395+
align-items: center;
396+
gap: 4px;
397+
height: 28px;
398+
padding: 4px 8px 4px 4px;
399+
border: 0;
400+
border-radius: 4px;
401+
background: transparent;
402+
color: var(--v2-text-text-base);
403+
}
404+
405+
.settings-v2-models-group-trigger:focus-visible {
406+
outline: 2px solid var(--v2-border-border-focus);
407+
outline-offset: 2px;
408+
}
409+
410+
@media (hover: hover) {
411+
.settings-v2-models-group-trigger:not(:disabled):hover {
412+
background-color: var(--v2-background-bg-layer-02);
413+
}
414+
}
415+
416+
.settings-v2-models-group-trigger:disabled {
417+
cursor: default;
418+
}
419+
420+
.settings-v2-models-group-chevron {
421+
display: flex;
422+
width: 20px;
423+
height: 20px;
424+
flex-shrink: 0;
425+
align-items: center;
426+
justify-content: center;
427+
color: var(--v2-icon-icon-muted);
428+
}
429+
430+
.settings-v2-models-group-label {
380431
display: flex;
381432
align-items: center;
382433
gap: 8px;
383-
padding-bottom: 8px;
384434
}
385435

386436
.settings-v2-models .settings-v2-section-title {
@@ -394,6 +444,10 @@
394444
color: var(--v2-icon-icon-base);
395445
}
396446

447+
.settings-v2-models [data-component="settings-v2-list"] {
448+
padding-inline: 16px;
449+
}
450+
397451
.settings-v2-models .settings-v2-section-title + [data-component="settings-v2-list"] {
398452
margin-top: 0;
399453
}

0 commit comments

Comments
 (0)