mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-06 16:03:22 +00:00
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
import { DataProvider } from "@opencode-ai/ui/context"
|
|
import { showToast } from "@/utils/toast"
|
|
import { base64Encode } from "@opencode-ai/core/util/encode"
|
|
import { useLocation, useNavigate, useParams } from "@solidjs/router"
|
|
import { type Accessor, createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js"
|
|
import { useLanguage } from "@/context/language"
|
|
import { LocalProvider } from "@/context/local"
|
|
import { SDKProvider } from "@/context/sdk"
|
|
import { useSync } from "@/context/sync"
|
|
import { decode64 } from "@/utils/base64"
|
|
import { Schema } from "effect"
|
|
import type { ServerConnection } from "@/context/server"
|
|
import { sessionHref } from "@/utils/session-route"
|
|
|
|
export function DirectoryDataProvider(
|
|
props: ParentProps<{
|
|
directory: string | Accessor<string>
|
|
draftID?: string
|
|
server?: Accessor<ServerConnection.Key | undefined>
|
|
}>,
|
|
) {
|
|
const location = useLocation()
|
|
const navigate = useNavigate()
|
|
const params = useParams()
|
|
const sync = useSync()
|
|
const directory = () => (typeof props.directory === "function" ? props.directory() : props.directory)
|
|
const slug = createMemo(() => base64Encode(directory()))
|
|
const href = (sessionID: string) => {
|
|
const server = props.server?.()
|
|
if (server) return sessionHref(server, sessionID)
|
|
return `/${slug()}/session/${sessionID}`
|
|
}
|
|
|
|
createEffect(() => {
|
|
// A draft lives at /new-session?draftId=… and has no directory segment to normalize.
|
|
if (props.draftID || props.server?.()) return
|
|
const next = sync().data.path.directory
|
|
if (!next || next === directory()) return
|
|
const path = location.pathname.slice(slug().length + 1)
|
|
navigate(`/${base64Encode(next)}${path}${location.search}${location.hash}`, { replace: true })
|
|
})
|
|
|
|
createResource(
|
|
() => params.id,
|
|
(id) =>
|
|
sync()
|
|
.session.sync(id)
|
|
.catch(() => {}),
|
|
)
|
|
|
|
return (
|
|
<DataProvider
|
|
data={sync().data}
|
|
directory={directory()}
|
|
onNavigateToSession={(sessionID: string) => navigate(href(sessionID))}
|
|
onSessionHref={href}
|
|
>
|
|
<LocalProvider>{props.children}</LocalProvider>
|
|
</DataProvider>
|
|
)
|
|
}
|
|
|
|
export const ProjectDirString = Schema.String.pipe(Schema.brand("ProjectDirString"))
|
|
export type ProjectDirString = Schema.Schema.Type<typeof ProjectDirString>
|
|
|
|
export function decodeDirectory(dir: string): ProjectDirString | undefined {
|
|
const decoded = decode64(dir)
|
|
if (!decoded) return
|
|
return ProjectDirString.make(decoded)
|
|
}
|
|
|
|
export default function Layout(props: ParentProps) {
|
|
const params = useParams()
|
|
const language = useLanguage()
|
|
const navigate = useNavigate()
|
|
let invalid = ""
|
|
|
|
const resolved = createMemo(() => {
|
|
if (!params.dir) return ""
|
|
return decodeDirectory(params.dir) ?? ""
|
|
})
|
|
|
|
createEffect(() => {
|
|
const dir = params.dir
|
|
if (!dir) return
|
|
if (resolved()) {
|
|
invalid = ""
|
|
return
|
|
}
|
|
if (invalid === dir) return
|
|
invalid = dir
|
|
showToast({
|
|
variant: "error",
|
|
title: language.t("common.requestFailed"),
|
|
description: language.t("directory.error.invalidUrl"),
|
|
})
|
|
navigate("/", { replace: true })
|
|
})
|
|
|
|
return (
|
|
<Show when={resolved()} keyed>
|
|
{(resolved) => (
|
|
<SDKProvider directory={resolved}>
|
|
<DirectoryDataProvider directory={resolved}>{props.children}</DirectoryDataProvider>
|
|
</SDKProvider>
|
|
)}
|
|
</Show>
|
|
)
|
|
}
|