Dax Raad 3 месяцев назад
Родитель
Сommit
56452d886d

+ 39 - 39
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>
   )
 }

+ 1 - 1
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()) {

+ 1 - 4
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>
             )
           })}

+ 95 - 58
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()
+    },
+  }
+}