mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
wip(desktop): progress
This commit is contained in:
parent
e845eedbc3
commit
634fd62b25
2 changed files with 121 additions and 14 deletions
17
packages/desktop/src/components/link.tsx
Normal file
17
packages/desktop/src/components/link.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue