summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src/context
diff options
context:
space:
mode:
Diffstat (limited to 'packages/desktop/src/context')
-rw-r--r--packages/desktop/src/context/dialog.tsx80
-rw-r--r--packages/desktop/src/context/layout.tsx69
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", {})
- },
- },
}
},
})