summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-12 20:19:14 -0600
committerAdam <[email protected]>2026-02-12 20:19:26 -0600
commitfb7b2f6b4d66d14177b5c0168049863842665925 (patch)
tree403262dbfbcd85b44c491a24b5eb06c7ac8125b1
parente0f1c3c20efb60f19f36e2c8df87dfd30fd2523e (diff)
downloadopencode-fb7b2f6b4d66d14177b5c0168049863842665925.tar.gz
opencode-fb7b2f6b4d66d14177b5c0168049863842665925.zip
feat(app): toggle all provider models
-rw-r--r--packages/app/src/components/dialog-manage-models.tsx32
-rw-r--r--packages/app/src/i18n/en.ts1
-rw-r--r--packages/ui/src/components/list.tsx7
-rw-r--r--packages/ui/src/components/switch.tsx2
4 files changed, 37 insertions, 5 deletions
diff --git a/packages/app/src/components/dialog-manage-models.tsx b/packages/app/src/components/dialog-manage-models.tsx
index d4d4af0f1..ace79e38a 100644
--- a/packages/app/src/components/dialog-manage-models.tsx
+++ b/packages/app/src/components/dialog-manage-models.tsx
@@ -1,6 +1,7 @@
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch"
+import { Tooltip } from "@opencode-ai/ui/tooltip"
import { Button } from "@opencode-ai/ui/button"
import type { Component } from "solid-js"
import { useLocal } from "@/context/local"
@@ -18,6 +19,14 @@ export const DialogManageModels: Component = () => {
dialog.show(() => <DialogSelectProvider />)
}
const providerRank = (id: string) => popularProviders.indexOf(id)
+ const providerList = (providerID: string) => local.model.list().filter((x) => x.provider.id === providerID)
+ const providerVisible = (providerID: string) =>
+ providerList(providerID).every((x) => local.model.visible({ modelID: x.id, providerID: x.provider.id }))
+ const setProviderVisibility = (providerID: string, checked: boolean) => {
+ providerList(providerID).forEach((x) => {
+ local.model.setVisibility({ modelID: x.id, providerID: x.provider.id }, checked)
+ })
+ }
return (
<Dialog
@@ -36,7 +45,28 @@ export const DialogManageModels: Component = () => {
items={local.model.list()}
filterKeys={["provider.name", "name", "id"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
- groupBy={(x) => x.provider.name}
+ groupBy={(x) => x.provider.id}
+ groupHeader={(group) => {
+ const provider = group.items[0].provider
+ return (
+ <>
+ <span>{provider.name}</span>
+ <Tooltip
+ placement="top"
+ value={language.t("dialog.model.manage.provider.toggle", { provider: provider.name })}
+ >
+ <Switch
+ class="-mr-1"
+ checked={providerVisible(provider.id)}
+ onChange={(checked) => setProviderVisibility(provider.id, checked)}
+ hideLabel
+ >
+ {provider.name}
+ </Switch>
+ </Tooltip>
+ </>
+ )
+ }}
sortGroupsBy={(a, b) => {
const aRank = providerRank(a.items[0].provider.id)
const bRank = providerRank(b.items[0].provider.id)
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts
index c138c7b61..99513edaa 100644
--- a/packages/app/src/i18n/en.ts
+++ b/packages/app/src/i18n/en.ts
@@ -109,6 +109,7 @@ export const dict = {
"dialog.model.empty": "No model results",
"dialog.model.manage": "Manage models",
"dialog.model.manage.description": "Customize which models appear in the model selector.",
+ "dialog.model.manage.provider.toggle": "Toggle all {{provider}} models",
"dialog.model.unpaid.freeModels.title": "Free models provided by OpenCode",
"dialog.model.unpaid.addMore.title": "Add more models from popular providers",
diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx
index abd557220..aa2347037 100644
--- a/packages/ui/src/components/list.tsx
+++ b/packages/ui/src/components/list.tsx
@@ -45,6 +45,7 @@ export interface ListProps<T> extends FilteredListProps<T> {
itemWrapper?: (item: T, node: JSX.Element) => JSX.Element
divider?: boolean
add?: ListAddProps
+ groupHeader?: (group: { category: string; items: T[] }) => JSX.Element
}
export interface ListRef {
@@ -206,7 +207,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
)
}
- function GroupHeader(groupProps: { category: string }): JSX.Element {
+ function GroupHeader(groupProps: { group: { category: string; items: T[] } }): JSX.Element {
const [stuck, setStuck] = createSignal(false)
const [header, setHeader] = createSignal<HTMLDivElement | undefined>(undefined)
@@ -228,7 +229,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
return (
<div data-slot="list-header" data-stuck={stuck()} ref={setHeader}>
- {groupProps.category}
+ {props.groupHeader?.(groupProps.group) ?? groupProps.group.category}
</div>
)
}
@@ -323,7 +324,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
return (
<div data-slot="list-group">
<Show when={group.category}>
- <GroupHeader category={group.category} />
+ <GroupHeader group={group} />
</Show>
<div data-slot="list-items">
<For each={group.items}>
diff --git a/packages/ui/src/components/switch.tsx b/packages/ui/src/components/switch.tsx
index a8600aef4..f4f95baf5 100644
--- a/packages/ui/src/components/switch.tsx
+++ b/packages/ui/src/components/switch.tsx
@@ -10,7 +10,7 @@ export interface SwitchProps extends ParentProps<ComponentProps<typeof Kobalte>>
export function Switch(props: SwitchProps) {
const [local, others] = splitProps(props, ["children", "class", "hideLabel", "description"])
return (
- <Kobalte {...others} data-component="switch">
+ <Kobalte {...others} class={local.class} data-component="switch">
<Kobalte.Input data-slot="switch-input" />
<Show when={local.children}>
<Kobalte.Label data-slot="switch-label" classList={{ "sr-only": local.hideLabel }}>