summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/desktop/src/app.tsx78
-rw-r--r--packages/desktop/src/hooks/use-providers.ts2
-rw-r--r--packages/desktop/src/pages/directory-layout.tsx5
-rw-r--r--packages/ui/src/context/dialog.tsx153
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()
+ },
+ }
+}