diff options
| author | Dax Raad <[email protected]> | 2025-12-15 17:13:00 -0500 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-12-15 17:13:20 -0500 |
| commit | 56452d886d8f34841900ba969d489b5fa49bbad7 (patch) | |
| tree | 014fa543ea5922cc31e0f32cfd40e88cb8e4e35f | |
| parent | f3e64cfb190cbb063460709143ce7eb682c556f9 (diff) | |
| download | opencode-56452d886d8f34841900ba969d489b5fa49bbad7.tar.gz opencode-56452d886d8f34841900ba969d489b5fa49bbad7.zip | |
fix dialog root complexity
| -rw-r--r-- | packages/desktop/src/app.tsx | 78 | ||||
| -rw-r--r-- | packages/desktop/src/hooks/use-providers.ts | 2 | ||||
| -rw-r--r-- | packages/desktop/src/pages/directory-layout.tsx | 5 | ||||
| -rw-r--r-- | packages/ui/src/context/dialog.tsx | 153 |
4 files changed, 136 insertions, 102 deletions
diff --git a/packages/desktop/src/app.tsx b/packages/desktop/src/app.tsx index bc6f082a4..789a8fa2d 100644 --- a/packages/desktop/src/app.tsx +++ b/packages/desktop/src/app.tsx @@ -12,7 +12,7 @@ import { GlobalSDKProvider } from "@/context/global-sdk" import { TerminalProvider } from "@/context/terminal" import { PromptProvider } from "@/context/prompt" import { NotificationProvider } from "@/context/notification" -import { DialogProvider, DialogRoot } from "@opencode-ai/ui/context/dialog" +import { DialogProvider } from "@opencode-ai/ui/context/dialog" import { CommandProvider } from "@/context/command" import Layout from "@/pages/layout" import Home from "@/pages/home" @@ -36,46 +36,46 @@ const url = export function App() { return ( - <MarkedProvider> - <DiffComponentProvider component={Diff}> - <GlobalSDKProvider url={url}> - <GlobalSyncProvider> - <LayoutProvider> - <NotificationProvider> - <MetaProvider> - <Font /> - <Router - root={(props) => ( - <DialogProvider> + <DialogProvider> + <MarkedProvider> + <DiffComponentProvider component={Diff}> + <GlobalSDKProvider url={url}> + <GlobalSyncProvider> + <LayoutProvider> + <NotificationProvider> + <MetaProvider> + <Font /> + <Router + root={(props) => ( <CommandProvider> <Layout>{props.children}</Layout> </CommandProvider> - </DialogProvider> - )} - > - <Route path="/" component={Home} /> - <Route path="/:dir" component={DirectoryLayout}> - <Route path="/" component={() => <Navigate href="session" />} /> - <Route - path="/session/:id?" - component={(p) => ( - <Show when={p.params.id || true} keyed> - <TerminalProvider> - <PromptProvider> - <Session /> - </PromptProvider> - </TerminalProvider> - </Show> - )} - /> - </Route> - </Router> - </MetaProvider> - </NotificationProvider> - </LayoutProvider> - </GlobalSyncProvider> - </GlobalSDKProvider> - </DiffComponentProvider> - </MarkedProvider> + )} + > + <Route path="/" component={Home} /> + <Route path="/:dir" component={DirectoryLayout}> + <Route path="/" component={() => <Navigate href="session" />} /> + <Route + path="/session/:id?" + component={(p) => ( + <Show when={p.params.id || true} keyed> + <TerminalProvider> + <PromptProvider> + <Session /> + </PromptProvider> + </TerminalProvider> + </Show> + )} + /> + </Route> + </Router> + </MetaProvider> + </NotificationProvider> + </LayoutProvider> + </GlobalSyncProvider> + </GlobalSDKProvider> + </DiffComponentProvider> + </MarkedProvider> + </DialogProvider> ) } diff --git a/packages/desktop/src/hooks/use-providers.ts b/packages/desktop/src/hooks/use-providers.ts index 501ff9d0c..4a73fa055 100644 --- a/packages/desktop/src/hooks/use-providers.ts +++ b/packages/desktop/src/hooks/use-providers.ts @@ -6,8 +6,8 @@ import { createMemo } from "solid-js" export const popularProviders = ["opencode", "anthropic", "github-copilot", "openai", "google", "openrouter", "vercel"] export function useProviders() { - const params = useParams() const globalSync = useGlobalSync() + const params = useParams() const currentDirectory = createMemo(() => base64Decode(params.dir ?? "")) const providers = createMemo(() => { if (currentDirectory()) { diff --git a/packages/desktop/src/pages/directory-layout.tsx b/packages/desktop/src/pages/directory-layout.tsx index 0dbb3f6d6..c909a373d 100644 --- a/packages/desktop/src/pages/directory-layout.tsx +++ b/packages/desktop/src/pages/directory-layout.tsx @@ -6,7 +6,6 @@ import { LocalProvider } from "@/context/local" import { base64Decode } from "@opencode-ai/util/encode" import { DataProvider } from "@opencode-ai/ui/context" import { iife } from "@opencode-ai/util/iife" -import { DialogRoot } from "@opencode-ai/ui/context/dialog" export default function Layout(props: ParentProps) { const params = useParams() @@ -21,9 +20,7 @@ export default function Layout(props: ParentProps) { const sync = useSync() return ( <DataProvider data={sync.data} directory={directory()}> - <LocalProvider> - <DialogRoot>{props.children}</DialogRoot> - </LocalProvider> + <LocalProvider>{props.children}</LocalProvider> </DataProvider> ) })} diff --git a/packages/ui/src/context/dialog.tsx b/packages/ui/src/context/dialog.tsx index af5da06f9..fae0c57b4 100644 --- a/packages/ui/src/context/dialog.tsx +++ b/packages/ui/src/context/dialog.tsx @@ -1,79 +1,116 @@ -import { For, Show, type JSX } from "solid-js" -import { createStore } from "solid-js/store" -import { createSimpleContext } from "@opencode-ai/ui/context" +import { + createContext, + createMemo, + createSignal, + getOwner, + Owner, + ParentProps, + runWithOwner, + Show, + useContext, + type JSX, +} from "solid-js" +import { Dialog as Kobalte } from "@kobalte/core/dialog" -type DialogElement = JSX.Element | (() => JSX.Element) +type DialogElement = () => JSX.Element -export const { use: useDialog, provider: DialogProvider } = createSimpleContext({ - name: "Dialog", - init: () => { - const [store, setStore] = createStore({ - stack: [] as { - element: DialogElement - onClose?: () => void - }[], - }) +const Context = createContext<ReturnType<typeof init>>() - return { - get stack() { - return store.stack - }, - push(element: DialogElement, onClose?: () => void) { - setStore("stack", (s) => [...s, { element, onClose }]) - }, - pop() { - const current = store.stack.at(-1) - current?.onClose?.() - setStore("stack", store.stack.slice(0, -1)) - }, - replace(element: DialogElement, onClose?: () => void) { - for (const item of store.stack) { - item.onClose?.() - } - setStore("stack", [{ element, onClose }]) - }, - clear() { - for (const item of store.stack) { - item.onClose?.() - } - setStore("stack", []) - }, - } - }, -}) +function init() { + const [store, setStore] = createSignal< + { + element: DialogElement + onClose?: () => void + owner: Owner + }[] + >([]) -import { Dialog as Kobalte } from "@kobalte/core/dialog" + return { + get stack() { + return store() + }, + push(element: DialogElement, owner: Owner, onClose?: () => void) { + setStore((s) => [...s, { element, onClose, owner }]) + }, + pop() { + const current = store().at(-1) + current?.onClose?.() + setStore((stack) => stack.slice(0, -1)) + }, + replace(element: DialogElement, owner: Owner, onClose?: () => void) { + for (const item of store()) { + item.onClose?.() + } + setStore([{ element, onClose, owner }]) + }, + clear() { + for (const item of store()) { + item.onClose?.() + } + setStore([]) + }, + } +} -export function DialogRoot(props: { children?: JSX.Element }) { - const dialog = useDialog() +export function DialogProvider(props: ParentProps) { + const ctx = init() + const last = createMemo(() => ctx.stack.at(-1)) return ( - <> + <Context.Provider value={ctx}> {props.children} - <Show when={dialog.stack.length > 0}> - <div data-component="dialog-stack"> - <For each={dialog.stack}> - {(item, index) => ( - <Show when={index() === dialog.stack.length - 1}> + <div data-component="dialog-stack"> + <Show when={last()}> + {(item) => + runWithOwner(item().owner, () => { + return ( <Kobalte modal defaultOpen onOpenChange={(open) => { if (!open) { - item.onClose?.() - dialog.pop() + item().onClose?.() + ctx.pop() } }} > <Kobalte.Portal> <Kobalte.Overlay data-component="dialog-overlay" /> - {typeof item.element === "function" ? item.element() : item.element} + {item().element()} </Kobalte.Portal> </Kobalte> - </Show> - )} - </For> - </div> - </Show> - </> + ) + }) + } + </Show> + </div> + </Context.Provider> ) } + +export function useDialog() { + const ctx = useContext(Context) + const owner = getOwner() + if (!owner) { + throw new Error("useDialog must be used within a DialogProvider") + } + if (!ctx) { + throw new Error("useDialog must be used within a DialogProvider") + } + return { + get stack() { + return ctx.stack + }, + replace(element: DialogElement, onClose?: () => void) { + ctx.replace(element, owner, onClose) + }, + push(element: DialogElement, onClose?: () => void) { + ctx.push(element, owner, onClose) + }, + pop() { + ctx.pop() + }, + clear() { + ctx.clear() + }, + } +} |
