diff options
Diffstat (limited to 'packages/desktop/src/context')
| -rw-r--r-- | packages/desktop/src/context/dialog.tsx | 80 | ||||
| -rw-r--r-- | packages/desktop/src/context/layout.tsx | 69 |
2 files changed, 84 insertions, 65 deletions
diff --git a/packages/desktop/src/context/dialog.tsx b/packages/desktop/src/context/dialog.tsx new file mode 100644 index 000000000..cc49764fe --- /dev/null +++ b/packages/desktop/src/context/dialog.tsx @@ -0,0 +1,80 @@ +import { createEffect, For, onCleanup, Show, type JSX } from "solid-js" +import { createStore } from "solid-js/store" +import { createSimpleContext } from "@opencode-ai/ui/context" + +type DialogElement = JSX.Element | (() => JSX.Element) + +export const { use: useDialog, provider: DialogProvider } = createSimpleContext({ + name: "Dialog", + init: () => { + const [store, setStore] = createStore({ + stack: [] as { + element: DialogElement + onClose?: () => void + }[], + }) + + function handleKeyDown(e: KeyboardEvent) { + if (e.key === "Escape" && store.stack.length > 0) { + const current = store.stack.at(-1)! + current.onClose?.() + setStore("stack", store.stack.slice(0, -1)) + e.preventDefault() + e.stopPropagation() + } + } + + createEffect(() => { + document.addEventListener("keydown", handleKeyDown, true) + onCleanup(() => { + document.removeEventListener("keydown", handleKeyDown, true) + }) + }) + + 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", []) + }, + } + }, +}) + +export function DialogRoot(props: { children?: JSX.Element }) { + const dialog = useDialog() + return ( + <> + {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}> + {typeof item.element === "function" ? item.element() : item.element} + </Show> + )} + </For> + </div> + </Show> + </> + ) +} diff --git a/packages/desktop/src/context/layout.tsx b/packages/desktop/src/context/layout.tsx index 587276c53..925bf4d4c 100644 --- a/packages/desktop/src/context/layout.tsx +++ b/packages/desktop/src/context/layout.tsx @@ -1,5 +1,5 @@ -import { createStore, produce } from "solid-js/store" -import { batch, createMemo, onMount } from "solid-js" +import { createStore } from "solid-js/store" +import { createMemo, onMount } from "solid-js" import { createSimpleContext } from "@opencode-ai/ui/context" import { makePersisted } from "@solid-primitives/storage" import { useGlobalSync } from "./global-sync" @@ -22,8 +22,6 @@ export function getAvatarColors(key?: string) { } } -type Dialog = "provider" | "model" | "connect" | "manage-models" - export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({ name: "Layout", init: () => { @@ -45,22 +43,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, }), { - name: "layout.v1", + name: "layout.v2", }, ) - const [ephemeral, setEphemeral] = createStore<{ - connect: { - provider?: string - state?: "pending" | "complete" | "error" - error?: string - } - dialog: { - open?: Dialog - } - }>({ - connect: {}, - dialog: {}, - }) + const usedColors = new Set<AvatarColorKey>() function pickAvailableColor(): AvatarColorKey { @@ -169,53 +155,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("review", "state", "tab") }, }, - dialog: { - opened: createMemo(() => ephemeral.dialog?.open), - open(dialog: Dialog) { - setEphemeral("dialog", "open", dialog) - }, - close(dialog: Dialog) { - if (ephemeral.dialog.open === dialog) { - setEphemeral( - produce((state) => { - state.dialog.open = undefined - state.connect = {} - }), - ) - } - }, - connect(provider: string) { - setEphemeral( - produce((state) => { - state.dialog.open = "connect" - state.connect = { provider, state: "pending" } - }), - ) - }, - }, - connect: { - provider: createMemo(() => ephemeral.connect.provider), - state: createMemo(() => ephemeral.connect.state), - complete() { - setEphemeral( - produce((state) => { - state.dialog.open = "model" - state.connect.state = "complete" - }), - ) - }, - error(message: string) { - setEphemeral( - produce((state) => { - state.connect.state = "error" - state.connect.error = message - }), - ) - }, - clear() { - setEphemeral("connect", {}) - }, - }, } }, }) |
