diff options
Diffstat (limited to 'packages/desktop/src/pages/layout.tsx')
| -rw-r--r-- | packages/desktop/src/pages/layout.tsx | 108 |
1 files changed, 105 insertions, 3 deletions
diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 3ff3abb0e..10d4cbfda 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -9,6 +9,7 @@ import { Avatar } from "@opencode-ai/ui/avatar" import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { Button } from "@opencode-ai/ui/button" import { Icon } from "@opencode-ai/ui/icon" +import { ProviderIcon } from "@opencode-ai/ui/provider-icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip } from "@opencode-ai/ui/tooltip" import { Collapsible } from "@opencode-ai/ui/collapsible" @@ -29,6 +30,10 @@ import { useDragDropContext, } from "@thisbeyond/solid-dnd" import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" +import { SelectDialog } from "@opencode-ai/ui/select-dialog" +import { Tag } from "@opencode-ai/ui/tag" +import { IconName } from "@opencode-ai/ui/icons/provider" +import { popularProviders, useProviders } from "@/hooks/use-providers" export default function Layout(props: ParentProps) { const [store, setStore] = createStore({ @@ -44,6 +49,7 @@ export default function Layout(props: ParentProps) { const currentDirectory = createMemo(() => base64Decode(params.dir ?? "")) const sessions = createMemo(() => globalSync.child(currentDirectory())[0].session ?? []) const currentSession = createMemo(() => sessions().find((s) => s.id === params.id)) + const providers = useProviders() function navigateToProject(directory: string | undefined) { if (!directory) return @@ -82,12 +88,21 @@ export default function Layout(props: ParentProps) { } } + async function connectProvider() { + layout.dialog.open("provider") + } + createEffect(() => { if (!params.dir || !params.id) return const directory = base64Decode(params.dir) setStore("lastSession", directory, params.id) }) + createEffect(() => { + const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48 + document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`) + }) + function getDraggableId(event: unknown): string | undefined { if (typeof event !== "object" || event === null) return undefined if (!("draggable" in event)) return undefined @@ -465,10 +480,44 @@ export default function Layout(props: ParentProps) { </DragDropProvider> </div> <div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3"> + <Switch> + <Match when={!providers().connected().length && layout.sidebar.opened()}> + <div class="rounded-md bg-background-stronger shadow-xs-border-base"> + <div class="p-3 flex flex-col gap-2"> + <div class="text-12-medium text-text-strong">Getting started</div> + <div class="text-text-base">OpenCode includes free models so you can start immediately.</div> + <div class="text-text-base">Connect any provider to use models, inc. Claude, GPT, Gemini etc.</div> + </div> + <Tooltip placement="right" value="Connect provider" inactive={layout.sidebar.opened()}> + <Button + class="flex w-full text-left justify-start text-12-medium text-text-strong stroke-[1.5px] rounded-lg rounded-t-none shadow-none border-t border-border-weak-base pl-2.25 pb-px" + size="large" + icon="plus-small" + onClick={connectProvider} + > + <Show when={layout.sidebar.opened()}>Connect provider</Show> + </Button> + </Tooltip> + </div> + </Match> + <Match when={true}> + <Tooltip placement="right" value="Connect provider" inactive={layout.sidebar.opened()}> + <Button + class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg px-2" + variant="ghost" + size="large" + icon="plus-small" + onClick={connectProvider} + > + <Show when={layout.sidebar.opened()}>Connect provider</Show> + </Button> + </Tooltip> + </Match> + </Switch> <Show when={platform.openDirectoryPickerDialog}> <Tooltip placement="right" value="Open project" inactive={layout.sidebar.opened()}> <Button - class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg" + class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg px-2" variant="ghost" size="large" icon="folder-add-left" @@ -481,7 +530,7 @@ export default function Layout(props: ParentProps) { <Tooltip placement="right" value="Settings" inactive={layout.sidebar.opened()}> <Button disabled - class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg" + class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg px-2" variant="ghost" size="large" icon="settings-gear" @@ -494,7 +543,7 @@ export default function Layout(props: ParentProps) { as={"a"} href="https://opencode.ai/desktop-feedback" target="_blank" - class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg" + class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg px-2" variant="ghost" size="large" icon="bubble-5" @@ -505,6 +554,59 @@ export default function Layout(props: ParentProps) { </div> </div> <main class="size-full overflow-x-hidden flex flex-col items-start">{props.children}</main> + <Show when={layout.dialog.opened() === "provider"}> + <SelectDialog + defaultOpen + title="Connect provider" + placeholder="Search providers" + activeIcon="plus-small" + key={(x) => x?.id} + items={providers().all} + // current={local.model.current()} + filterKeys={["id", "name"]} + groupBy={(x) => (popularProviders.includes(x.id) ? "Popular" : "Other")} + sortBy={(a, b) => { + if (popularProviders.includes(a.id) && popularProviders.includes(b.id)) + return popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id) + return a.name.localeCompare(b.name) + }} + sortGroupsBy={(a, b) => { + if (a.category === "Popular" && b.category !== "Popular") return -1 + if (b.category === "Popular" && a.category !== "Popular") return 1 + return 0 + }} + // onSelect={(x) => } + onOpenChange={(open) => { + if (open) { + layout.dialog.open("provider") + } else { + layout.dialog.close("provider") + } + }} + > + {(i) => ( + <div class="px-1.25 w-full flex items-center gap-x-4"> + <ProviderIcon + data-slot="list-item-extra-icon" + id={i.id as IconName} + // TODO: clean this up after we update icon in models.dev + classList={{ + "text-icon-weak-base": true, + "size-4 mx-0.5": i.id === "opencode", + "size-5": i.id !== "opencode", + }} + /> + <span>{i.name}</span> + <Show when={i.id === "opencode"}> + <Tag>Recommended</Tag> + </Show> + <Show when={i.id === "anthropic"}> + <div class="text-14-regular text-text-weak">Connect with Claude Pro/Max or API key</div> + </Show> + </div> + )} + </SelectDialog> + </Show> </div> </div> ) |
