From 2fbdff9f4124f8b87ddd33f197a20f24eb1496e4 Mon Sep 17 00:00:00 2001 From: usrnk1 <7547651+usrnk1@users.noreply.github.com> Date: Fri, 16 Oct 2026 12:20:52 +0200 Subject: [PATCH] feat(app): flatten manage models into v2 rows (#56029) --- packages/app/src/providers/models/manage.css | 117 +++++++++++++++++++ packages/app/src/providers/models/manage.tsx | 33 +++--- 2 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 packages/app/src/providers/models/manage.css diff --git a/packages/app/src/providers/models/manage.css b/packages/app/src/providers/models/manage.css new file mode 100644 index 00000000000..6cdbbb383ce --- /dev/null +++ b/packages/app/src/providers/models/manage.css @@ -0,0 +1,117 @@ +/* Outranks the deeply nested base dialog header rule. */ +[data-component="dialog-v2"] + [data-slot="dialog-container"] + .settings-manage-models-dialog[data-slot="dialog-content"] + [data-slot="dialog-header"] { + padding-bottom: 12px; +} + +.manage-models-search { + flex-shrink: 0; + /* The 1px top keeps the field's outside focus outline from clipping against the body. */ + padding: 1px 12px 8px; +} + +.settings-manage-models-dialog .settings-panel.settings-models { + height: 100%; + gap: 0; + padding: 0 8px 8px; +} + +.settings-manage-models-dialog .settings-models .settings-section { + gap: 4px; + padding-block: 8px; +} + +.settings-manage-models-dialog .settings-models .settings-section[data-expanded] { + padding-bottom: 8px; +} + +.settings-manage-models-dialog .settings-models .settings-section + .settings-section { + border-top: 0.5px solid var(--v2-border-border-base); +} + +.settings-manage-models-dialog .provider-model-groups { + gap: 8px; +} + +.settings-manage-models-dialog .provider-model-group-header { + padding: 4px; + border-radius: 0; +} + +.settings-manage-models-dialog .provider-model-group-header:has(:focus-visible) { + background: transparent; +} + +.settings-manage-models-dialog .provider-model-group-trigger { + min-height: 36px; + padding-block: 0; + padding-inline: 12px 40px; + border-radius: 6px; +} + +.settings-manage-models-dialog .provider-model-group-header:has(:focus-visible) .provider-model-group-trigger { + background: var(--v2-overlay-simple-overlay-hover); +} + +.settings-manage-models-dialog .provider-model-group-actions { + position: absolute; + top: 50%; + inset-inline-end: 16px; + padding: 0; + transform: translateY(-50%); +} + +@media (hover: hover) { + .settings-manage-models-dialog .provider-model-group-header:hover { + background: transparent; + } + + .settings-manage-models-dialog .provider-model-group-header:hover .provider-model-group-trigger:not(:disabled) { + background: var(--v2-overlay-simple-overlay-hover); + } +} + +.settings-manage-models-dialog .provider-model-group-models { + padding: 0 8px 8px; +} + +.settings-manage-models-dialog .provider-model-group-models > .manage-models-rows { + padding-top: 4px; + border-top: 0.5px solid var(--v2-border-border-base); +} + +.manage-models-rows { + display: flex; + flex-direction: column; + gap: 1px; +} + +.manage-models-rows .search-list-row { + position: relative; + height: 36px; + padding-inline: 8px; + border-radius: 6px; +} + +.manage-models-rows .search-list-row:not(:last-child)::after { + content: ""; + position: absolute; + inset-inline: 8px; + bottom: -0.75px; + height: 0.5px; + background: var(--v2-border-border-base); + pointer-events: none; +} + +@media (hover: hover) { + .manage-models-rows .search-list-row:hover { + background: var(--v2-overlay-simple-overlay-hover); + } + + .manage-models-rows .search-list-row:hover::after, + .manage-models-rows .search-list-row:has(+ .search-list-row:hover)::after { + opacity: 0; + } +} diff --git a/packages/app/src/providers/models/manage.tsx b/packages/app/src/providers/models/manage.tsx index 74cf546b246..37b7412ac3e 100644 --- a/packages/app/src/providers/models/manage.tsx +++ b/packages/app/src/providers/models/manage.tsx @@ -11,10 +11,10 @@ import { useLanguage } from "@/runtime/i18n/language" import { useDialog } from "@opencode/ui/context/dialog" import { DialogConnectProvider } from "@/providers/connect/dialog" import { decode64 } from "@/runtime/persistence/base64" -import { SettingsList } from "@/settings/list" -import { SettingsRow } from "@/settings/row" import { consoleModelGroup, ProviderModelSections } from "@/providers/models/provider-group" import "@/settings/settings.css" +import "@/shell/search-list.css" +import "./manage.css" type ModelItem = ReturnType["model"]["list"]>[number] @@ -71,24 +71,29 @@ export const DialogManageModels: Component = () => { const expanded = (key: string) => searching() || !store.collapsed[key] function ModelRows(props: { items: ModelItem[] }) { + const visible = (item: ModelItem) => local.model.visible({ modelID: item.id, providerID: item.provider.id }) + return ( - +
{(item) => ( - -
- setModelVisibility(item, checked)} - hideLabel - > +
event.preventDefault()} + onClick={() => setModelVisibility(item, !visible(item))} + > +
+ {item.name} +
+
event.stopPropagation()}> + setModelVisibility(item, checked)} hideLabel> {item.name}
- +
)} - +
) } @@ -104,7 +109,7 @@ export const DialogManageModels: Component = () => { -
+
-
+