wip(desktop): progress

This commit is contained in:
Adam 2025-12-11 12:01:37 -06:00
parent e845eedbc3
commit 634fd62b25
No known key found for this signature in database
GPG key ID: 9CB48779AF150E75
2 changed files with 121 additions and 14 deletions

View file

@ -0,0 +1,17 @@
import { ComponentProps, splitProps } from "solid-js"
import { usePlatform } from "@/context/platform"
export interface LinkProps extends ComponentProps<"button"> {
href: string
}
export function Link(props: LinkProps) {
const platform = usePlatform()
const [local, rest] = splitProps(props, ["href", "children"])
return (
<button class="text-text-strong underline" onClick={() => platform.openLink(local.href)} {...rest}>
{local.children}
</button>
)
}

View file

@ -36,6 +36,7 @@ import { IconName } from "@opencode-ai/ui/icons/provider"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { Dialog } from "@opencode-ai/ui/dialog"
import { iife } from "@opencode-ai/util/iife"
import { Link } from "@/components/link"
import { List, ListRef } from "@opencode-ai/ui/list"
import { Input } from "@opencode-ai/ui/input"
import { showToast, Toast } from "@opencode-ai/ui/toast"
@ -637,6 +638,8 @@ export default function Layout(props: ParentProps) {
error: undefined as string | undefined,
})
const methodIndex = createMemo(() => methods().findIndex((x) => x.label === store.method?.label))
async function selectMethod(index: number) {
const method = methods()[index]
setStore(
@ -652,10 +655,13 @@ export default function Layout(props: ParentProps) {
setStore("state", "pending")
const start = Date.now()
await globalSDK.client.provider.oauth
.authorize({
providerID: providerID(),
method: index,
})
.authorize(
{
providerID: providerID(),
method: index,
},
{ throwOnError: true },
)
.then((x) => {
const elapsed = Date.now() - start
const delay = 1000 - elapsed
@ -731,7 +737,16 @@ export default function Layout(props: ParentProps) {
<div class="flex flex-col gap-6 px-2.5 pb-3">
<div class="px-2.5 flex gap-4 items-center">
<ProviderIcon id={providerID() as IconName} class="size-5 shrink-0 icon-strong-base" />
<div class="text-16-medium text-text-strong">Connect {provider().name}</div>
<div class="text-16-medium text-text-strong">
<Switch>
<Match
when={providerID() === "anthropic" && store.method?.label?.toLowerCase().includes("max")}
>
Login with Claude Pro/Max
</Match>
<Match when={true}>Connect {provider().name}</Match>
</Switch>
</div>
</div>
<div class="px-2.5 pb-10 flex flex-col gap-6">
<Switch>
@ -756,7 +771,6 @@ export default function Layout(props: ParentProps) {
data-slot="list-item-extra-icon"
/>
</div>
{/* TODO: add checkmark thing */}
<span>{i.label}</span>
</div>
)}
@ -833,13 +847,9 @@ export default function Layout(props: ParentProps) {
</div>
<div class="text-14-regular text-text-base">
Visit{" "}
<button
tabIndex={-1}
class="text-text-strong underline"
onClick={() => platform.openLink("https://opencode.ai/zen")}
>
<Link href="https://opencode.ai/zen" tabIndex={-1}>
opencode.ai/zen
</button>{" "}
</Link>{" "}
to collect your API key.
</div>
</div>
@ -873,8 +883,88 @@ export default function Layout(props: ParentProps) {
</Match>
<Match when={store.method?.type === "oauth"}>
<Switch>
<Match when={store.authorization?.method === "code"}>Code {store.authorization?.url}</Match>
<Match when={store.authorization?.method === "auto"}>Auto {store.authorization?.url}</Match>
<Match when={store.authorization?.method === "code"}>
{iife(() => {
const [formStore, setFormStore] = createStore({
value: "",
error: undefined as string | undefined,
})
onMount(() => {
if (store.authorization?.method === "code" && store.authorization?.url) {
platform.openLink(store.authorization.url)
}
})
async function handleSubmit(e: SubmitEvent) {
e.preventDefault()
const form = e.currentTarget as HTMLFormElement
const formData = new FormData(form)
const code = formData.get("code") as string
if (!code?.trim()) {
setFormStore("error", "Authorization code is required")
return
}
setFormStore("error", undefined)
const { error } = await globalSDK.client.provider.oauth.callback({
providerID: providerID(),
method: methodIndex(),
code,
})
if (!error) {
await globalSDK.client.global.dispose()
setTimeout(() => {
showToast({
variant: "success",
icon: "circle-check",
title: `${provider().name} connected`,
description: `${provider().name} models are now available to use.`,
})
layout.connect.complete()
}, 500)
return
}
setFormStore("error", "Invalid authorization code")
}
return (
<div class="flex flex-col gap-6">
<div class="text-14-regular text-text-base">
Visit <Link href={store.authorization!.url}>this link</Link> to collect your
authorization code to connect your account and use {provider().name} models in
OpenCode.
</div>
<form onSubmit={handleSubmit} class="flex flex-col items-start gap-4">
<Input
autofocus
type="text"
label={`${store.method?.label} authorization code`}
placeholder="Authorization code"
name="code"
value={formStore.value}
onChange={setFormStore.bind(null, "value")}
validationState={formStore.error ? "invalid" : undefined}
error={formStore.error}
/>
<Button class="w-auto" type="submit" size="large" variant="primary">
Submit
</Button>
</form>
</div>
)
})}
</Match>
<Match when={store.authorization?.method === "auto"}>
<div class="flex flex-col gap-6">
<div class="text-14-regular text-text-base">
Visit <Link href={store.authorization!.url}>this link</Link> and enter the code below
to connect your account and use {provider().name} models in OpenCode.
</div>
</div>
</Match>
</Switch>
</Match>
</Switch>