feat(desktop): Adding Provider Icons (#8215)
This commit is contained in:
@@ -7,6 +7,8 @@ import { Button } from "@opencode-ai/ui/button"
|
|||||||
import { Tag } from "@opencode-ai/ui/tag"
|
import { Tag } from "@opencode-ai/ui/tag"
|
||||||
import { Dialog } from "@opencode-ai/ui/dialog"
|
import { Dialog } from "@opencode-ai/ui/dialog"
|
||||||
import { List } from "@opencode-ai/ui/list"
|
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 { DialogSelectProvider } from "./dialog-select-provider"
|
||||||
import { DialogManageModels } from "./dialog-manage-models"
|
import { DialogManageModels } from "./dialog-manage-models"
|
||||||
|
|
||||||
@@ -35,6 +37,12 @@ const ModelList: Component<{
|
|||||||
filterKeys={["provider.name", "name", "id"]}
|
filterKeys={["provider.name", "name", "id"]}
|
||||||
sortBy={(a, b) => a.name.localeCompare(b.name)}
|
sortBy={(a, b) => a.name.localeCompare(b.name)}
|
||||||
groupBy={(x) => x.provider.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) => {
|
sortGroupsBy={(a, b) => {
|
||||||
if (a.category === "Recent" && b.category !== "Recent") return -1
|
if (a.category === "Recent" && b.category !== "Recent") return -1
|
||||||
if (b.category === "Recent" && a.category !== "Recent") return 1
|
if (b.category === "Recent" && a.category !== "Recent") return 1
|
||||||
@@ -52,7 +60,8 @@ const ModelList: Component<{
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(i) => (
|
{(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>
|
<span class="truncate">{i.name}</span>
|
||||||
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
|
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
|
||||||
<Tag>Free</Tag>
|
<Tag>Free</Tag>
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import { useSync } from "@/context/sync"
|
|||||||
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
import { FileIcon } from "@opencode-ai/ui/file-icon"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
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 { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { Select } from "@opencode-ai/ui/select"
|
import { Select } from "@opencode-ai/ui/select"
|
||||||
@@ -1560,6 +1562,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
fallback={
|
fallback={
|
||||||
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
||||||
<Button as="div" variant="ghost" onClick={() => dialog.show(() => <DialogSelectModelUnpaid />)}>
|
<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"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
<Icon name="chevron-down" size="small" />
|
<Icon name="chevron-down" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1569,6 +1577,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
<ModelSelectorPopover>
|
<ModelSelectorPopover>
|
||||||
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
<TooltipKeybind placement="top" title="Choose model" keybind={command.keybind("model.choose")}>
|
||||||
<Button as="div" variant="ghost">
|
<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"}
|
{local.model.current()?.name ?? "Select model"}
|
||||||
<Icon name="chevron-down" size="small" />
|
<Icon name="chevron-down" size="small" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -1583,10 +1597,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
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()}
|
onClick={() => local.model.variant.cycle()}
|
||||||
>
|
>
|
||||||
<span class="capitalize text-12-regular">{local.model.variant.current() ?? "Default"}</span>
|
{local.model.variant.current() ?? "Default"}
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -123,13 +123,13 @@
|
|||||||
|
|
||||||
&[data-size="normal"] {
|
&[data-size="normal"] {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
&[data-icon] {
|
&[data-icon] {
|
||||||
padding: 0 12px 0 4px;
|
padding: 0 12px 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
line-height: var(--line-height-large);
|
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
/* text-12-medium */
|
/* text-12-medium */
|
||||||
@@ -137,7 +137,6 @@
|
|||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-small);
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
line-height: var(--line-height-large); /* 166.667% */
|
|
||||||
letter-spacing: var(--letter-spacing-normal);
|
letter-spacing: var(--letter-spacing-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,15 @@ export interface ListSearchProps {
|
|||||||
autofocus?: boolean
|
autofocus?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ListGroup<T> {
|
||||||
|
category: string
|
||||||
|
items: T[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface ListProps<T> extends FilteredListProps<T> {
|
export interface ListProps<T> extends FilteredListProps<T> {
|
||||||
class?: string
|
class?: string
|
||||||
children: (item: T) => JSX.Element
|
children: (item: T) => JSX.Element
|
||||||
|
groupHeader?: (group: ListGroup<T>) => JSX.Element
|
||||||
emptyMessage?: string
|
emptyMessage?: string
|
||||||
onKeyEvent?: (event: KeyboardEvent, item: T | undefined) => void
|
onKeyEvent?: (event: KeyboardEvent, item: T | undefined) => void
|
||||||
onMove?: (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,
|
setScrollRef,
|
||||||
})
|
})
|
||||||
|
|
||||||
function GroupHeader(props: { category: string }): JSX.Element {
|
function GroupHeader(groupProps: { category: string; children?: JSX.Element }): JSX.Element {
|
||||||
const [stuck, setStuck] = createSignal(false)
|
const [stuck, setStuck] = createSignal(false)
|
||||||
const [header, setHeader] = createSignal<HTMLDivElement | undefined>(undefined)
|
const [header, setHeader] = createSignal<HTMLDivElement | undefined>(undefined)
|
||||||
|
|
||||||
@@ -138,7 +144,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-slot="list-header" data-stuck={stuck()} ref={setHeader}>
|
<div data-slot="list-header" data-stuck={stuck()} ref={setHeader}>
|
||||||
{props.category}
|
{groupProps.children ?? groupProps.category}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -185,7 +191,7 @@ export function List<T>(props: ListProps<T> & { ref?: (ref: ListRef) => void })
|
|||||||
{(group) => (
|
{(group) => (
|
||||||
<div data-slot="list-group">
|
<div data-slot="list-group">
|
||||||
<Show when={group.category}>
|
<Show when={group.category}>
|
||||||
<GroupHeader category={group.category} />
|
<GroupHeader category={group.category}>{props.groupHeader?.(group)}</GroupHeader>
|
||||||
</Show>
|
</Show>
|
||||||
<div data-slot="list-items">
|
<div data-slot="list-items">
|
||||||
<For each={group.items}>
|
<For each={group.items}>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import { Accordion } from "./accordion"
|
|||||||
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
||||||
import { FileIcon } from "./file-icon"
|
import { FileIcon } from "./file-icon"
|
||||||
import { Icon } from "./icon"
|
import { Icon } from "./icon"
|
||||||
|
import { ProviderIcon } from "./provider-icon"
|
||||||
|
import type { IconName } from "./provider-icons/types"
|
||||||
import { IconButton } from "./icon-button"
|
import { IconButton } from "./icon-button"
|
||||||
import { Tooltip } from "./tooltip"
|
import { Tooltip } from "./tooltip"
|
||||||
import { Card } from "./card"
|
import { Card } from "./card"
|
||||||
@@ -498,7 +500,13 @@ export function SessionTurn(
|
|||||||
<span data-slot="session-turn-badge">{(msg() as UserMessage).agent}</span>
|
<span data-slot="session-turn-badge">{(msg() as UserMessage).agent}</span>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={(msg() as UserMessage).model?.modelID}>
|
<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>
|
</Show>
|
||||||
<span data-slot="session-turn-badge">{(msg() as UserMessage).variant || "default"}</span>
|
<span data-slot="session-turn-badge">{(msg() as UserMessage).variant || "default"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user