import { Popover as Kobalte } from "@kobalte/core/popover" import { Component, ComponentProps, createEffect, createMemo, JSX, onCleanup, Show, ValidComponent } from "solid-js" import { createStore } from "solid-js/store" import { useLocal } from "@/context/local" import { useDialog } from "@opencode-ai/ui/context/dialog" import { popularProviders } from "@/hooks/use-providers" import { Button } from "@opencode-ai/ui/button" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tag } from "@opencode-ai/ui/tag" import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { Tooltip } from "@opencode-ai/ui/tooltip" import { DialogSelectProvider } from "./dialog-select-provider" import { DialogManageModels } from "./dialog-manage-models" import { ModelTooltip } from "./model-tooltip" import { useLanguage } from "@/context/language" const ModelList: Component<{ provider?: string class?: string onSelect: () => void action?: JSX.Element }> = (props) => { const local = useLocal() const language = useLanguage() const models = createMemo(() => local.model .list() .filter((m) => local.model.visible({ modelID: m.id, providerID: m.provider.id })) .filter((m) => (props.provider ? m.provider.id === props.provider : true)), ) return ( `${x.provider.id}:${x.id}`} items={models} current={local.model.current()} filterKeys={["provider.name", "name", "id"]} sortBy={(a, b) => a.name.localeCompare(b.name)} groupBy={(x) => x.provider.name} sortGroupsBy={(a, b) => { const aProvider = a.items[0].provider.id const bProvider = b.items[0].provider.id if (popularProviders.includes(aProvider) && !popularProviders.includes(bProvider)) return -1 if (!popularProviders.includes(aProvider) && popularProviders.includes(bProvider)) return 1 return popularProviders.indexOf(aProvider) - popularProviders.indexOf(bProvider) }} itemWrapper={(item, node) => ( } > {node} )} onSelect={(x) => { local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, { recent: true, }) props.onSelect() }} > {(i) => (
{i.name} {language.t("model.tag.free")} {language.t("model.tag.latest")}
)}
) } export function ModelSelectorPopover(props: { provider?: string children?: JSX.Element triggerAs?: T triggerProps?: ComponentProps }) { const [store, setStore] = createStore<{ open: boolean dismiss: "escape" | "outside" | null trigger?: HTMLElement content?: HTMLElement }>({ open: false, dismiss: null, trigger: undefined, content: undefined, }) const dialog = useDialog() const handleManage = () => { setStore("open", false) dialog.show(() => ) } const handleConnectProvider = () => { setStore("open", false) dialog.show(() => ) } const language = useLanguage() createEffect(() => { if (!store.open) return const inside = (node: Node | null | undefined) => { if (!node) return false const el = store.content if (el && el.contains(node)) return true const anchor = store.trigger if (anchor && anchor.contains(node)) return true return false } const onKeyDown = (event: KeyboardEvent) => { if (event.key !== "Escape") return setStore("dismiss", "escape") setStore("open", false) event.preventDefault() event.stopPropagation() } const onPointerDown = (event: PointerEvent) => { const target = event.target if (!(target instanceof Node)) return if (inside(target)) return setStore("dismiss", "outside") setStore("open", false) } const onFocusIn = (event: FocusEvent) => { if (!store.content) return const target = event.target if (!(target instanceof Node)) return if (inside(target)) return setStore("dismiss", "outside") setStore("open", false) } window.addEventListener("keydown", onKeyDown, true) window.addEventListener("pointerdown", onPointerDown, true) window.addEventListener("focusin", onFocusIn, true) onCleanup(() => { window.removeEventListener("keydown", onKeyDown, true) window.removeEventListener("pointerdown", onPointerDown, true) window.removeEventListener("focusin", onFocusIn, true) }) }) return ( { if (next) setStore("dismiss", null) setStore("open", next) }} modal={false} placement="top-start" gutter={8} > setStore("trigger", el)} as={props.triggerAs ?? "div"} {...(props.triggerProps as any)} > {props.children} setStore("content", el)} class="w-72 h-80 flex flex-col p-2 rounded-md border border-border-base bg-surface-raised-stronger-non-alpha shadow-md z-50 outline-none overflow-hidden" onEscapeKeyDown={(event) => { setStore("dismiss", "escape") setStore("open", false) event.preventDefault() event.stopPropagation() }} onPointerDownOutside={() => { setStore("dismiss", "outside") setStore("open", false) }} onFocusOutside={() => { setStore("dismiss", "outside") setStore("open", false) }} onCloseAutoFocus={(event) => { if (store.dismiss === "outside") event.preventDefault() setStore("dismiss", null) }} > {language.t("dialog.model.select.title")} setStore("open", false)} class="p-1" action={
} />
) } export const DialogSelectModel: Component<{ provider?: string }> = (props) => { const dialog = useDialog() const language = useLanguage() return ( dialog.show(() => )} > {language.t("command.provider.connect")} } > dialog.close()} /> ) }