summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src/pages
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-12-10 14:48:08 -0600
committerAdam <[email protected]>2025-12-10 15:17:03 -0600
commit190fa4c87aa2b3f954a419f716add1fc29e4011e (patch)
tree835e91a7457c4d92fd8d3118d225f12843b80a5b /packages/desktop/src/pages
parent91d743ef9a5c346fe17bb857db68dca92a6e9ba1 (diff)
downloadopencode-190fa4c87aa2b3f954a419f716add1fc29e4011e.tar.gz
opencode-190fa4c87aa2b3f954a419f716add1fc29e4011e.zip
wip(desktop): progress
Diffstat (limited to 'packages/desktop/src/pages')
-rw-r--r--packages/desktop/src/pages/home.tsx4
-rw-r--r--packages/desktop/src/pages/layout.tsx90
2 files changed, 62 insertions, 32 deletions
diff --git a/packages/desktop/src/pages/home.tsx b/packages/desktop/src/pages/home.tsx
index 4aac241e1..205ffd815 100644
--- a/packages/desktop/src/pages/home.tsx
+++ b/packages/desktop/src/pages/home.tsx
@@ -38,7 +38,7 @@ export default function Home() {
<div class="mx-auto mt-55">
<Logo class="w-xl opacity-12" />
<Switch>
- <Match when={sync.data.projects.length > 0}>
+ <Match when={sync.data.project.length > 0}>
<div class="mt-20 w-full flex flex-col gap-4">
<div class="flex gap-2 items-center justify-between pl-3">
<div class="text-14-medium text-text-strong">Recent projects</div>
@@ -50,7 +50,7 @@ export default function Home() {
</div>
<ul class="flex flex-col gap-2">
<For
- each={sync.data.projects
+ each={sync.data.project
.toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
.slice(0, 5)}
>
diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx
index 3e0094756..2ea6c4ba0 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"
@@ -31,6 +32,9 @@ import {
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"
+
+const popularProviders = ["opencode", "anthropic", "github-copilot", "openai", "google", "openrouter", "vercel"]
export default function Layout(props: ParentProps) {
const [store, setStore] = createStore({
@@ -46,15 +50,18 @@ 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 = createMemo(() => globalSync.data.providers)
- const hasProviders = createMemo(() => {
- const [projectStore] = globalSync.child(currentDirectory())
- return projectStore.provider.filter((p) => p.id !== "opencode").length > 0
- })
-
- createEffect(() => {
- console.log(providers())
+ const providers = createMemo(() => {
+ if (currentDirectory()) {
+ const [projectStore] = globalSync.child(currentDirectory())
+ return projectStore.provider
+ }
+ return globalSync.data.provider
})
+ const connectedProviders = createMemo(() =>
+ providers().all.filter(
+ (p) => providers().connected.includes(p.id) && Object.values(p.models).find((m) => m.cost?.input),
+ ),
+ )
function navigateToProject(directory: string | undefined) {
if (!directory) return
@@ -93,7 +100,9 @@ export default function Layout(props: ParentProps) {
}
}
- async function connectProvider() {}
+ async function connectProvider() {
+ layout.dialog.open("provider")
+ }
createEffect(() => {
if (!params.dir || !params.id) return
@@ -484,7 +493,7 @@ export default function Layout(props: ParentProps) {
</div>
<div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3">
<Switch>
- <Match when={!hasProviders() && layout.sidebar.opened()}>
+ <Match when={!connectedProviders().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>
@@ -493,7 +502,7 @@ export default function Layout(props: ParentProps) {
</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-base stroke-[1.5px] rounded-lg rounded-t-none shadow-none border-t border-border-weak-base pl-[7px]"
+ 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}
@@ -506,7 +515,7 @@ export default function Layout(props: ParentProps) {
<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"
+ 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"
@@ -520,7 +529,7 @@ export default function Layout(props: ParentProps) {
<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"
@@ -533,7 +542,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"
@@ -546,7 +555,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"
@@ -557,32 +566,53 @@ 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={true}>
+ <Show when={layout.dialog.opened() === "provider"}>
<SelectDialog
defaultOpen
title="Connect provider"
placeholder="Search providers"
+ activeIcon="plus-small"
key={(x) => x?.id}
- items={providers()}
+ items={providers().all}
// current={local.model.current()}
- filterKeys={["provider.name", "name", "id"]}
- // groupBy={(x) => (local.model.recent().includes(x) ? "Recent" : x.provider.name)}
- // groupBy={(x) => x.provider.name}
- onSelect={(x) =>
- // local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, { recent: true })
- {
- return
+ 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="w-full flex items-center gap-x-2.5">
+ <div class="px-1.25 w-full flex items-center gap-x-4">
+ <ProviderIcon
+ 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.cost || i.cost?.input === 0}>
- <Tag>Free</Tag>
+ <Show when={i.id === "opencode"}>
+ <Tag>Recommended</Tag>
</Show>
- <Show when={i.latest}>
- <Tag>Latest</Tag>
+ <Show when={i.id === "anthropic"}>
+ <div class="text-14-regular text-text-weak">Connect with Claude Pro/Max or API key</div>
</Show>
</div>
)}