import { DataProvider } from "@opencode-ai/session-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, onCleanup, 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" import { useServerSync } from "@/context/server-sync" export function DirectoryDataProvider( props: ParentProps<{ directory: string | Accessor draftID?: string server?: Accessor }>, ) { const location = useLocation() const navigate = useNavigate() const params = useParams() const sync = useSync() const serverSync = useServerSync() 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(() => {}), ) createEffect(() => { const sessionID = params.id if (!sessionID) return serverSync().session.pin(sessionID) onCleanup(() => serverSync().session.unpin(sessionID)) }) return ( {(directory) => ( navigate(href(sessionID))} onSessionHref={href} > {props.children} )} ) } export const ProjectDirString = Schema.String.pipe(Schema.brand("ProjectDirString")) export type ProjectDirString = Schema.Schema.Type 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 ( {(resolved) => ( {props.children} )} ) }