From 5fd873a35a19de2f9e89c420158d5697746d2983 Mon Sep 17 00:00:00 2001 From: Aaron Iker Date: Mon, 22 Dec 2025 12:41:38 +0100 Subject: feat: polish dialog & list styles for the desktop app, add fixed logos from models.dev (#5925) --- packages/ui/src/components/dialog.css | 14 +- packages/ui/src/components/list.css | 216 +++++--- packages/ui/src/components/list.tsx | 36 +- .../ui/src/components/provider-icons/sprite.svg | 587 ++++++++++++--------- packages/ui/src/components/provider-icons/types.ts | 2 + 5 files changed, 499 insertions(+), 356 deletions(-) (limited to 'packages/ui/src/components') diff --git a/packages/ui/src/components/dialog.css b/packages/ui/src/components/dialog.css index 6fa71c64c..782f2570c 100644 --- a/packages/ui/src/components/dialog.css +++ b/packages/ui/src/components/dialog.css @@ -4,7 +4,7 @@ position: fixed; inset: 0; z-index: 50; - background-color: transparent; + background-color: hsl(from var(--background-base) h s l / 0.2); /* animation: overlayHide 250ms ease 100ms forwards; */ /**/ @@ -43,13 +43,14 @@ /* padding: 8px; */ /* padding: 8px 8px 0 8px; */ - border: 1px solid var(--border-base); + border: 1px solid hsl(from var(--border-base) h s l / 0.2); border-radius: var(--radius-xl); background: var(--surface-raised-stronger-non-alpha); + background-clip: padding-box; box-shadow: - 0 15px 45px 0 rgba(19, 16, 16, 0.22), - 0 3.35px 10.051px 0 rgba(19, 16, 16, 0.13), - 0 0.998px 2.993px 0 rgba(19, 16, 16, 0.09); + 0 15px 45px 0 rgba(19, 16, 16, 0.35), + 0 3.35px 10.051px 0 rgba(19, 16, 16, 0.25), + 0 0.998px 2.993px 0 rgba(19, 16, 16, 0.2); /* animation: contentHide 300ms ease-in forwards; */ /**/ @@ -59,8 +60,7 @@ [data-slot="dialog-header"] { display: flex; - padding: 16px; - padding-left: 20px; + padding: 16px 16px 16px 24px; justify-content: space-between; align-items: center; flex-shrink: 0; diff --git a/packages/ui/src/components/list.css b/packages/ui/src/components/list.css index 368065e53..1714b3513 100644 --- a/packages/ui/src/components/list.css +++ b/packages/ui/src/components/list.css @@ -1,17 +1,36 @@ +@property --bottom-fade { + syntax: ""; + inherits: false; + initial-value: 0px; +} + +@keyframes scroll { + 0% { + --bottom-fade: 20px; + } + 90% { + --bottom-fade: 20px; + } + 100% { + --bottom-fade: 0; + } +} + [data-component="list"] { display: flex; flex-direction: column; - gap: 20px; + gap: 12px; overflow: hidden; + padding: 0 12px; [data-slot="list-search"] { display: flex; - height: 40px; flex-shrink: 0; - padding: 4px 10px 4px 16px; + padding: 8px; align-items: center; gap: 12px; align-self: stretch; + margin-bottom: 4px; border-radius: var(--radius-md); background: var(--surface-base); @@ -19,11 +38,17 @@ [data-slot="list-search-container"] { display: flex; align-items: center; - gap: 16px; + gap: 8px; flex: 1 0 0; + max-height: 20px; [data-slot="list-search-input"] { width: 100%; + + &[data-slot="input-input"] { + line-height: 20px; + max-height: 20px; + } } } } @@ -31,88 +56,67 @@ [data-slot="list-scroll"] { display: flex; flex-direction: column; - gap: 20px; + gap: 12px; overflow-y: auto; + overscroll-behavior: contain; + mask: linear-gradient(to bottom, #ffff calc(100% - var(--bottom-fade)), #0000); + animation: scroll; + animation-timeline: --scroll; + scroll-timeline: --scroll y; scrollbar-width: none; -ms-overflow-style: none; &::-webkit-scrollbar { display: none; } - } - - [data-slot="list-empty-state"] { - display: flex; - padding: 32px 0px; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 8px; - align-self: stretch; - [data-slot="list-message"] { + [data-slot="list-empty-state"] { display: flex; + padding: 32px 0px; + flex-direction: column; justify-content: center; align-items: center; - gap: 2px; - color: var(--text-weak); - text-align: center; - - /* text-14-regular */ - font-family: var(--font-family-sans); - font-size: 14px; - font-style: normal; - font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 142.857% */ - letter-spacing: var(--letter-spacing-normal); - } + gap: 8px; + align-self: stretch; - [data-slot="list-filter"] { - color: var(--text-strong); - } - } + [data-slot="list-message"] { + display: flex; + justify-content: center; + align-items: center; + gap: 2px; + color: var(--text-weak); + text-align: center; - [data-slot="list-group"] { - position: relative; - display: flex; - flex-direction: column; + /* text-14-regular */ + font-family: var(--font-family-sans); + font-size: 14px; + font-style: normal; + font-weight: var(--font-weight-regular); + line-height: var(--line-height-large); /* 142.857% */ + letter-spacing: var(--letter-spacing-normal); + } - [data-slot="list-header"] { - display: flex; - z-index: 10; - height: 28px; - padding: 0 10px; - justify-content: space-between; - align-items: center; - align-self: stretch; - background: var(--surface-raised-stronger-non-alpha); - position: sticky; - top: 0; - - color: var(--text-base); - - /* text-14-medium */ - font-family: var(--font-family-sans); - font-size: 14px; - font-style: normal; - font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); /* 142.857% */ - letter-spacing: var(--letter-spacing-normal); + [data-slot="list-filter"] { + color: var(--text-strong); + } } - [data-slot="list-items"] { + [data-slot="list-group"] { + position: relative; display: flex; flex-direction: column; - align-items: flex-start; - align-self: stretch; - [data-slot="list-item"] { + [data-slot="list-header"] { display: flex; - width: 100%; - height: 28px; - padding: 4px 10px; + z-index: 10; + padding: 0 12px 8px 8px; + justify-content: space-between; align-items: center; - color: var(--text-strong); - scroll-margin-top: 28px; + align-self: stretch; + background: var(--surface-raised-stronger-non-alpha); + position: sticky; + top: 0; + + color: var(--text-base); /* text-14-medium */ font-family: var(--font-family-sans); @@ -122,30 +126,76 @@ line-height: var(--line-height-large); /* 142.857% */ letter-spacing: var(--letter-spacing-normal); - [data-slot="list-item-selected-icon"] { - color: var(--icon-strong-base); + &::after { + content: ""; + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 16px; + background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent); + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; } - [data-slot="list-item-active-icon"] { - display: none; - color: var(--icon-strong-base); + + &[data-stuck="true"]::after { + opacity: 1; } + } - &[data-active="true"] { - border-radius: var(--radius-md); - background: var(--surface-raised-base-hover); + [data-slot="list-items"] { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + + [data-slot="list-item"] { + display: flex; + width: 100%; + padding: 6px 8px 6px 8px; + align-items: center; + color: var(--text-strong); + scroll-margin-top: 28px; + + /* text-14-medium */ + font-family: var(--font-family-sans); + font-size: 14px; + font-style: normal; + font-weight: var(--font-weight-medium); + line-height: var(--line-height-large); /* 142.857% */ + letter-spacing: var(--letter-spacing-normal); + + [data-slot="list-item-selected-icon"] { + color: var(--icon-strong-base); + } [data-slot="list-item-active-icon"] { - display: block; + display: none; + color: var(--icon-strong-base); } + [data-slot="list-item-extra-icon"] { - display: block !important; - color: var(--icon-strong-base) !important; + color: var(--icon-base); + margin-left: -4px; + } + + &[data-active="true"] { + border-radius: var(--radius-md); + background: var(--surface-raised-base-hover); + [data-slot="list-item-active-icon"] { + display: block; + } + [data-slot="list-item-extra-icon"] { + display: block !important; + color: var(--icon-strong-base) !important; + } + } + &:active { + background: var(--surface-raised-base-active); + } + &:focus-visible { + outline: none; } - } - &:active { - background: var(--surface-raised-base-active); - } - &:focus-visible { - outline: none; } } } diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx index 0ed745f32..4f6df0faf 100644 --- a/packages/ui/src/components/list.tsx +++ b/packages/ui/src/components/list.tsx @@ -1,7 +1,7 @@ -import { createEffect, on, Show, For, type JSX, createSignal } from "solid-js" +import { type FilteredListProps, useFilteredList } from "@opencode-ai/ui/hooks" +import { createEffect, createSignal, For, type JSX, on, Show } from "solid-js" import { createStore } from "solid-js/store" -import { FilteredListProps, useFilteredList } from "@opencode-ai/ui/hooks" -import { Icon, IconProps } from "./icon" +import { Icon, type IconProps } from "./icon" import { IconButton } from "./icon-button" import { TextField } from "./text-field" @@ -149,7 +149,31 @@ export function List(props: ListProps & { ref?: (ref: ListRef) => void }) {(group) => (
-
{group.category}
+ {(() => { + const [stuck, setStuck] = createSignal(false) + return ( +
{ + createEffect(() => { + const scroll = scrollRef() + if (!scroll) return + const handler = () => { + const rect = el.getBoundingClientRect() + const scrollRect = scroll.getBoundingClientRect() + setStuck(rect.top <= scrollRect.top + 1 && scroll.scrollTop > 0) + } + scroll.addEventListener("scroll", handler, { passive: true }) + handler() + return () => scroll.removeEventListener("scroll", handler) + }) + }} + > + {group.category} +
+ ) + })()}
@@ -160,10 +184,14 @@ export function List(props: ListProps & { ref?: (ref: ListRef) => void }) data-active={props.key(item) === active()} data-selected={item === props.current} onClick={() => handleSelect(item, i())} + type="button" onMouseMove={() => { setStore("mouseActive", true) setActive(props.key(item)) }} + onMouseLeave={() => { + setActive(null) + }} > {props.children(item)} diff --git a/packages/ui/src/components/provider-icons/sprite.svg b/packages/ui/src/components/provider-icons/sprite.svg index 3bb4b9617..22e1223a1 100644 --- a/packages/ui/src/components/provider-icons/sprite.svg +++ b/packages/ui/src/components/provider-icons/sprite.svg @@ -1,37 +1,48 @@ - + - + - + + + + - + - + - + @@ -58,59 +69,45 @@ stroke-linejoin="round" > - + - - + + - + - - + @@ -137,12 +134,16 @@ stroke-linejoin="round" > - + + - - - - - + + - - SiliconFlow - - - + + - + + + + @@ -247,90 +251,135 @@ stroke-linejoin="round" > - + - + - + - - - - - - - + + + + + + + - - - - + + + - + - + + + + + - + - - Nebius - + + @@ -357,53 +406,51 @@ stroke-linejoin="round" > - + - + - + - + - + - - - - + + - - - - + + - + @@ -430,105 +477,123 @@ stroke-linejoin="round" > - + - + - - + - - - - - - - - - + + - + + + + + - + - + + + + - + - + - + @@ -555,40 +620,44 @@ stroke-linejoin="round" > - + - + - + - + - + - + @@ -615,49 +684,39 @@ stroke-linejoin="round" > - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + - + - - Baseten + - + - + @@ -737,32 +796,36 @@ stroke-linejoin="round" > - + - + - + - + - + diff --git a/packages/ui/src/components/provider-icons/types.ts b/packages/ui/src/components/provider-icons/types.ts index b3caf7edb..81fcc3678 100644 --- a/packages/ui/src/components/provider-icons/types.ts +++ b/packages/ui/src/components/provider-icons/types.ts @@ -6,6 +6,7 @@ export const iconNames = [ "zenmux", "zai", "zai-coding-plan", + "xiaomi", "xai", "wandb", "vultr", @@ -17,6 +18,7 @@ export const iconNames = [ "synthetic", "submodel", "siliconflow", + "siliconflow-cn", "scaleway", "sap-ai-core", "requesty", -- cgit v1.2.3