Browse Source

feat(desktop): Adding Provider Icons (#8215)

Daniel Polito 1 month ago
parent
commit
528291532b

+ 10 - 1
packages/app/src/components/dialog-select-model.tsx

@@ -7,6 +7,8 @@ import { Button } from "@opencode-ai/ui/button"
 import { Tag } from "@opencode-ai/ui/tag"
 import { Dialog } from "@opencode-ai/ui/dialog"
 import { List } from "@opencode-ai/ui/list"
+import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
+import type { IconName } from "@opencode-ai/ui/icons/provider"
 import { DialogSelectProvider } from "./dialog-select-provider"
 import { DialogManageModels } from "./dialog-manage-models"
 
@@ -35,6 +37,12 @@ const ModelList: Component<{
       filterKeys={["provider.name", "name", "id"]}
       sortBy={(a, b) => a.name.localeCompare(b.name)}
       groupBy={(x) => x.provider.name}
+      groupHeader={(group) => (
+        <div class="flex items-center gap-x-3">
+          <ProviderIcon data-slot="list-item-extra-icon" id={group.items[0].provider.id as IconName} />
+          <span>{group.category}</span>
+        </div>
+      )}
       sortGroupsBy={(a, b) => {
         if (a.category === "Recent" && b.category !== "Recent") return -1
         if (b.category === "Recent" && a.category !== "Recent") return 1
@@ -52,7 +60,8 @@ const ModelList: Component<{
       }}
     >
       {(i) => (
-        <div class="w-full flex items-center gap-x-2 text-13-regular">
+        <div class="w-full flex items-center gap-x-3 pl-1 text-13-regular">
+          <ProviderIcon data-slot="list-item-extra-icon" id={i.provider.id as IconName} />
           <span class="truncate">{i.name}</span>
           <Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
             <Tag>Free</Tag>

+ 16 - 2
packages/app/src/components/prompt-input.tsx

@@ -33,6 +33,8 @@ import { useSync } from "@/context/sync"
 import { FileIcon } from "@opencode-ai/ui/file-icon"
 import { Button } from "@opencode-ai/ui/button"
 import { Icon } from "@opencode-ai/ui/icon"
+import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
+import type { IconName } from "@opencode-ai/ui/icons/provider"
 import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
 import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Select } from "@opencode-ai/ui/select"
@@ -1560,6 +1562,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
                   fallback={
                     <TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
                       <Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
+                        <Show when={local.model.current()?.provider?.id}>
+                          <ProviderIcon
+                            id={local.model.current()!.provider.id as IconName}
+                            class="size-4 shrink-0"
+                          />
+                        </Show>
                         {local.model.current()?.name ?? "Select model"}
                         <Icon name="chevron-down" size="small" />
                       </Button>
@@ -1569,6 +1577,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
                   <ModelSelectorPopover>
                     <TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
                       <Button as="div" variant="ghost">
+                        <Show when={local.model.current()?.provider?.id}>
+                          <ProviderIcon
+                            id={local.model.current()!.provider.id as IconName}
+                            class="size-4 shrink-0"
+                          />
+                        </Show>
                         {local.model.current()?.name ?? "Select model"}
                         <Icon name="chevron-down" size="small" />
                       </Button>
@@ -1583,10 +1597,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
                   >
                     <Button
                       variant="ghost"
-                      class="text-text-base _hidden group-hover/prompt-input:inline-block"
+                      class="text-text-base _hidden group-hover/prompt-input:inline-block capitalize text-12-regular"
                       onClick={() => local.model.variant.cycle()}
                     >
-                      <span class="capitalize text-12-regular">{local.model.variant.current() ?? "Default"}</span>
+                      {local.model.variant.current() ?? "Default"}
                     </Button>
                   </TooltipKeybind>
                 </Show>

+ 1 - 2
packages/ui/src/components/button.css

@@ -123,13 +123,13 @@
 
   &[data-size="normal"] {
     height: 24px;
+    line-height: 24px;
     padding: 0 6px;
     &[data-icon] {
       padding: 0 12px 0 4px;
     }
 
     font-size: var(--font-size-small);
-    line-height: var(--line-height-large);
     gap: 6px;
 
     /* text-12-medium */
@@ -137,7 +137,6 @@
     font-size: var(--font-size-small);
     font-style: normal;
     font-weight: var(--font-weight-medium);
-    line-height: var(--line-height-large); /* 166.667% */
     letter-spacing: var(--letter-spacing-normal);
   }
 

+ 9 - 3
packages/ui/src/components/list.tsx

@@ -10,9 +10,15 @@ export interface ListSearchProps {
   autofocus?: boolean
 }
 
+export interface ListGroup<T> {
+  category: string
+  items: T[]
+}
+
 export interface ListProps<T> extends FilteredListProps<T> {
   class?: string
   children: (item: T) => JSX.Element
+  groupHeader?: (group: ListGroup<T>) => JSX.Element
   emptyMessage?: string
   onKeyEvent?: (event: KeyboardEvent, item: T | undefined) => void
   onMove?: (item: T | undefined) => void
@@ -116,7 +122,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
     setScrollRef,
   })
 
-  function GroupHeader(props: { category: string }): JSX.Element {
+  function GroupHeader(groupProps: { category: string; children?: JSX.Element }): JSX.Element {
     const [stuck, setStuck] = createSignal(false)
     const [header, setHeader] = createSignal<HTMLDivElement | undefined>(undefined)
 
@@ -138,7 +144,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
 
     return (
       <div data-slot="list-header" data-stuck={stuck()} ref={setHeader}>
-        {props.category}
+        {groupProps.children ?? groupProps.category}
       </div>
     )
   }
@@ -185,7 +191,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
             {(group) => (
               <div data-slot="list-group">
                 <Show when={group.category}>
-                  <GroupHeader category={group.category} />
+                  <GroupHeader category={group.category}>{props.groupHeader?.(group)}</GroupHeader>
                 </Show>
                 <div data-slot="list-items">
                   <For each={group.items}>

+ 9 - 1
packages/ui/src/components/session-turn.tsx

@@ -22,6 +22,8 @@ import { Accordion } from "./accordion"
 import { StickyAccordionHeader } from "./sticky-accordion-header"
 import { FileIcon } from "./file-icon"
 import { Icon } from "./icon"
+import { ProviderIcon } from "./provider-icon"
+import type { IconName } from "./provider-icons/types"
 import { IconButton } from "./icon-button"
 import { Tooltip } from "./tooltip"
 import { Card } from "./card"
@@ -498,7 +500,13 @@ export function SessionTurn(
                             <span data-slot="session-turn-badge">{(msg() as UserMessage).agent}</span>
                           </Show>
                           <Show when={(msg() as UserMessage).model?.modelID}>
-                            <span data-slot="session-turn-badge">{(msg() as UserMessage).model?.modelID}</span>
+                            <span data-slot="session-turn-badge" class="inline-flex items-center gap-1">
+                              <ProviderIcon
+                                id={(msg() as UserMessage).model!.providerID as IconName}
+                                class="size-3.5 shrink-0"
+                              />
+                              {(msg() as UserMessage).model?.modelID}
+                            </span>
                           </Show>
                           <span data-slot="session-turn-badge">{(msg() as UserMessage).variant || "default"}</span>
                         </div>