feat(desktop): collapse model provider sections (#39283)
This commit is contained in:
@@ -5,9 +5,12 @@ import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
|
|||||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
|
||||||
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
|
||||||
import { type Component, For, Show } from "solid-js"
|
import { type Component, For, Show } from "solid-js"
|
||||||
|
import { createStore } from "solid-js/store"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { useModels } from "@/context/models"
|
import { useModels } from "@/context/models"
|
||||||
|
import { useServerSDK } from "@/context/server-sdk"
|
||||||
import { popularProviders } from "@/hooks/use-providers"
|
import { popularProviders } from "@/hooks/use-providers"
|
||||||
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
import { SettingsListV2 } from "./parts/list"
|
import { SettingsListV2 } from "./parts/list"
|
||||||
import { SettingsRowV2 } from "./parts/row"
|
import { SettingsRowV2 } from "./parts/row"
|
||||||
import "./settings-v2.css"
|
import "./settings-v2.css"
|
||||||
@@ -19,6 +22,11 @@ const PROVIDER_ICON_SIZE = 16
|
|||||||
export const SettingsModelsV2: Component = () => {
|
export const SettingsModelsV2: Component = () => {
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const models = useModels()
|
const models = useModels()
|
||||||
|
const serverSdk = useServerSDK()
|
||||||
|
const [store, setStore] = persisted(
|
||||||
|
Persist.serverGlobal(serverSdk().scope, "settings-v2.models.providers"),
|
||||||
|
createStore({ collapsed: {} as Record<string, boolean> }),
|
||||||
|
)
|
||||||
|
|
||||||
const list = useFilteredList<ModelItem>({
|
const list = useFilteredList<ModelItem>({
|
||||||
items: (_filter) => models.list(),
|
items: (_filter) => models.list(),
|
||||||
@@ -94,41 +102,82 @@ export const SettingsModelsV2: Component = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<For each={list.grouped.latest}>
|
<For each={list.grouped.latest}>
|
||||||
{(group) => (
|
{(group) => {
|
||||||
<div class="settings-v2-section" data-component="settings-models-provider">
|
const searching = () => list.filter().length > 0
|
||||||
<div class="settings-v2-models-group-header">
|
const expanded = () => searching() || !store.collapsed[group.category]
|
||||||
<ProviderIcon
|
|
||||||
id={group.category}
|
return (
|
||||||
width={PROVIDER_ICON_SIZE}
|
<div
|
||||||
height={PROVIDER_ICON_SIZE}
|
class="settings-v2-section"
|
||||||
class="settings-v2-models-provider-icon shrink-0"
|
data-component="settings-models-provider"
|
||||||
/>
|
data-expanded={expanded() ? "" : undefined}
|
||||||
<h3 class="settings-v2-section-title">{group.items[0].provider.name}</h3>
|
>
|
||||||
|
<h3 class="settings-v2-models-group-header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="settings-v2-models-group-trigger"
|
||||||
|
aria-expanded={expanded()}
|
||||||
|
disabled={searching()}
|
||||||
|
onClick={() => setStore("collapsed", group.category, expanded())}
|
||||||
|
>
|
||||||
|
<span class="settings-v2-models-group-chevron">
|
||||||
|
<Show
|
||||||
|
when={expanded()}
|
||||||
|
fallback={
|
||||||
|
<svg width="5" height="6" viewBox="0 0 5 6" fill="none" aria-hidden="true">
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||||
|
<path
|
||||||
|
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"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</Show>
|
||||||
|
</span>
|
||||||
|
<span class="settings-v2-models-group-label">
|
||||||
|
<ProviderIcon
|
||||||
|
id={group.category}
|
||||||
|
width={PROVIDER_ICON_SIZE}
|
||||||
|
height={PROVIDER_ICON_SIZE}
|
||||||
|
class="settings-v2-models-provider-icon shrink-0"
|
||||||
|
/>
|
||||||
|
<span class="settings-v2-section-title">{group.items[0].provider.name}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
<Show when={expanded()}>
|
||||||
|
<SettingsListV2>
|
||||||
|
<For each={group.items}>
|
||||||
|
{(item) => {
|
||||||
|
const key = { providerID: item.provider.id, modelID: item.id }
|
||||||
|
return (
|
||||||
|
<SettingsRowV2 title={item.name} description="">
|
||||||
|
<div>
|
||||||
|
<Switch
|
||||||
|
checked={models.visible(key)}
|
||||||
|
onChange={(checked) => {
|
||||||
|
models.setVisibility(key, checked)
|
||||||
|
}}
|
||||||
|
hideLabel
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</SettingsRowV2>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</SettingsListV2>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<SettingsListV2>
|
)
|
||||||
<For each={group.items}>
|
}}
|
||||||
{(item) => {
|
|
||||||
const key = { providerID: item.provider.id, modelID: item.id }
|
|
||||||
return (
|
|
||||||
<SettingsRowV2 title={item.name} description="">
|
|
||||||
<div>
|
|
||||||
<Switch
|
|
||||||
checked={models.visible(key)}
|
|
||||||
onChange={(checked) => {
|
|
||||||
models.setVisibility(key, checked)
|
|
||||||
}}
|
|
||||||
hideLabel
|
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</Switch>
|
|
||||||
</div>
|
|
||||||
</SettingsRowV2>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
</SettingsListV2>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
</For>
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -373,14 +373,64 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-v2-models {
|
.settings-v2-models {
|
||||||
gap: 24px;
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models .settings-v2-section {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models .settings-v2-section[data-expanded] {
|
||||||
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-v2-models-group-header {
|
.settings-v2-models-group-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models-group-trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 4px 8px 4px 4px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--v2-text-text-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models-group-trigger:focus-visible {
|
||||||
|
outline: 2px solid var(--v2-border-border-focus);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.settings-v2-models-group-trigger:not(:disabled):hover {
|
||||||
|
background-color: var(--v2-background-bg-layer-02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models-group-trigger:disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models-group-chevron {
|
||||||
|
display: flex;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--v2-icon-icon-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-v2-models-group-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-v2-models .settings-v2-section-title {
|
.settings-v2-models .settings-v2-section-title {
|
||||||
@@ -394,6 +444,10 @@
|
|||||||
color: var(--v2-icon-icon-base);
|
color: var(--v2-icon-icon-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-v2-models [data-component="settings-v2-list"] {
|
||||||
|
padding-inline: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-v2-models .settings-v2-section-title + [data-component="settings-v2-list"] {
|
.settings-v2-models .settings-v2-section-title + [data-component="settings-v2-list"] {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user