diff options
| author | Dax Raad <[email protected]> | 2025-12-15 19:33:17 -0500 |
|---|---|---|
| committer | Dax Raad <[email protected]> | 2025-12-15 19:35:18 -0500 |
| commit | 112c58abf516e4523fb698b868a710c40b271559 (patch) | |
| tree | ba7f47f02cc5cda42e5ac0d5ae563be3e452ac68 /packages | |
| parent | 0dce5173ccca05aa7bd9eaabab78fe1f5f4d4360 (diff) | |
| download | opencode-112c58abf516e4523fb698b868a710c40b271559.tar.gz opencode-112c58abf516e4523fb698b868a710c40b271559.zip | |
tui: refactor dialog system to use single active dialog instead of stack
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/desktop/src/components/dialog-connect-provider.tsx | 22 | ||||
| -rw-r--r-- | packages/desktop/src/components/dialog-select-file.tsx | 2 | ||||
| -rw-r--r-- | packages/desktop/src/components/dialog-select-model-unpaid.tsx | 6 | ||||
| -rw-r--r-- | packages/desktop/src/components/dialog-select-model.tsx | 6 | ||||
| -rw-r--r-- | packages/desktop/src/components/dialog-select-provider.tsx | 2 | ||||
| -rw-r--r-- | packages/desktop/src/components/prompt-input.tsx | 4 | ||||
| -rw-r--r-- | packages/desktop/src/context/command.tsx | 6 | ||||
| -rw-r--r-- | packages/desktop/src/context/notification.tsx | 2 | ||||
| -rw-r--r-- | packages/desktop/src/pages/layout.tsx | 2 | ||||
| -rw-r--r-- | packages/desktop/src/pages/session.tsx | 8 | ||||
| -rw-r--r-- | packages/ui/src/context/dialog.tsx | 122 |
11 files changed, 78 insertions, 104 deletions
diff --git a/packages/desktop/src/components/dialog-connect-provider.tsx b/packages/desktop/src/components/dialog-connect-provider.tsx index 4660e1398..0d6737815 100644 --- a/packages/desktop/src/components/dialog-connect-provider.tsx +++ b/packages/desktop/src/components/dialog-connect-provider.tsx @@ -108,20 +108,18 @@ export function DialogConnectProvider(props: { provider: string }) { async function complete() { await globalSDK.client.global.dispose() - setTimeout(() => { - showToast({ - variant: "success", - icon: "circle-check", - title: `${provider().name} connected`, - description: `${provider().name} models are now available to use.`, - }) - dialog.replace(() => <DialogSelectModel provider={props.provider} />) - }, 1000) + dialog.close() + showToast({ + variant: "success", + icon: "circle-check", + title: `${provider().name} connected`, + description: `${provider().name} models are now available to use.`, + }) } function goBack() { if (methods().length === 1) { - dialog.replace(() => <DialogSelectProvider />) + dialog.show(() => <DialogSelectProvider />) return } if (store.authorization) { @@ -133,7 +131,7 @@ export function DialogConnectProvider(props: { provider: string }) { setStore("methodIndex", undefined) return } - dialog.replace(() => <DialogSelectProvider />) + dialog.show(() => <DialogSelectProvider />) } return ( @@ -352,7 +350,7 @@ export function DialogConnectProvider(props: { provider: string }) { }) if (result.error) { // TODO: show error - dialog.clear() + dialog.close() return } await complete() diff --git a/packages/desktop/src/components/dialog-select-file.tsx b/packages/desktop/src/components/dialog-select-file.tsx index b719e15d2..61c518719 100644 --- a/packages/desktop/src/components/dialog-select-file.tsx +++ b/packages/desktop/src/components/dialog-select-file.tsx @@ -27,7 +27,7 @@ export function DialogSelectFile() { if (path) { tabs().open("file://" + path) } - dialog.clear() + dialog.close() }} > {(i) => ( diff --git a/packages/desktop/src/components/dialog-select-model-unpaid.tsx b/packages/desktop/src/components/dialog-select-model-unpaid.tsx index 7cdb24915..77e493d3c 100644 --- a/packages/desktop/src/components/dialog-select-model-unpaid.tsx +++ b/packages/desktop/src/components/dialog-select-model-unpaid.tsx @@ -42,7 +42,7 @@ export const DialogSelectModelUnpaid: Component = () => { local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, { recent: true, }) - dialog.clear() + dialog.close() }} > {(i) => ( @@ -75,7 +75,7 @@ export const DialogSelectModelUnpaid: Component = () => { }} onSelect={(x) => { if (!x) return - dialog.replace(() => <DialogConnectProvider provider={x.id} />) + dialog.show(() => <DialogConnectProvider provider={x.id} />) }} > {(i) => ( @@ -105,7 +105,7 @@ export const DialogSelectModelUnpaid: Component = () => { class="w-full justify-start px-[11px] py-3.5 gap-4.5 text-14-medium" icon="dot-grid" onClick={() => { - dialog.replace(() => <DialogSelectProvider />) + dialog.show(() => <DialogSelectProvider />) }} > View all providers diff --git a/packages/desktop/src/components/dialog-select-model.tsx b/packages/desktop/src/components/dialog-select-model.tsx index f0b2e6db9..622ab15fb 100644 --- a/packages/desktop/src/components/dialog-select-model.tsx +++ b/packages/desktop/src/components/dialog-select-model.tsx @@ -28,7 +28,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { class="h-7 -my-1 text-14-medium" icon="plus-small" tabIndex={-1} - onClick={() => dialog.replace(() => <DialogSelectProvider />)} + onClick={() => dialog.show(() => <DialogSelectProvider />)} > Connect provider </Button> @@ -57,7 +57,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined, { recent: true, }) - dialog.clear() + dialog.close() }} > {(i) => ( @@ -75,7 +75,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { <Button variant="ghost" class="ml-3 mt-5 mb-6 text-text-base self-start" - onClick={() => dialog.replace(() => <DialogManageModels />)} + onClick={() => dialog.show(() => <DialogManageModels />)} > Manage models </Button> diff --git a/packages/desktop/src/components/dialog-select-provider.tsx b/packages/desktop/src/components/dialog-select-provider.tsx index 8da10b1d5..52fac7073 100644 --- a/packages/desktop/src/components/dialog-select-provider.tsx +++ b/packages/desktop/src/components/dialog-select-provider.tsx @@ -34,7 +34,7 @@ export const DialogSelectProvider: Component = () => { }} onSelect={(x) => { if (!x) return - dialog.replace(() => <DialogConnectProvider provider={x.id} />) + dialog.show(() => <DialogConnectProvider provider={x.id} />) }} > {(i) => ( diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index f2821c3c7..6e147242d 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -864,9 +864,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { as="div" variant="ghost" onClick={() => - dialog.replace(() => - providers.paid().length > 0 ? <DialogSelectModel /> : <DialogSelectModelUnpaid />, - ) + dialog.show(() => (providers.paid().length > 0 ? <DialogSelectModel /> : <DialogSelectModelUnpaid />)) } > {local.model.current()?.name ?? "Select model"} diff --git a/packages/desktop/src/context/command.tsx b/packages/desktop/src/context/command.tsx index d4ef8e166..8fd76ee21 100644 --- a/packages/desktop/src/context/command.tsx +++ b/packages/desktop/src/context/command.tsx @@ -128,7 +128,7 @@ function DialogCommand(props: { options: CommandOption[] }) { groupBy={(x) => x.category ?? ""} onSelect={(option) => { if (option) { - dialog.clear() + dialog.close() option.onSelect?.("palette") } }} @@ -174,8 +174,8 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex const suspended = () => suspendCount() > 0 const showPalette = () => { - if (dialog.stack.length === 0) { - dialog.replace(() => <DialogCommand options={options().filter((x) => !x.disabled)} />) + if (!dialog.active) { + dialog.show(() => <DialogCommand options={options().filter((x) => !x.disabled)} />) } } diff --git a/packages/desktop/src/context/notification.tsx b/packages/desktop/src/context/notification.tsx index 045361630..ee15bc34a 100644 --- a/packages/desktop/src/context/notification.tsx +++ b/packages/desktop/src/context/notification.tsx @@ -59,7 +59,7 @@ export const { use: useNotification, provider: NotificationProvider } = createSi time: Date.now(), viewed: false, } - switch (event?.type) { + switch (event.type) { case "session.idle": { const sessionID = event.properties.sessionID const [syncStore] = globalSync.child(directory) diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 2f0c6e050..27852967a 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -217,7 +217,7 @@ export default function Layout(props: ParentProps) { ]) function connectProvider() { - dialog.replace(() => <DialogSelectProvider />) + dialog.show(() => <DialogSelectProvider />) } function navigateToProject(directory: string | undefined) { diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 9167ef7b7..390872d36 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -176,7 +176,7 @@ export default function Page() { category: "File", keybind: "mod+p", slash: "open", - onSelect: () => dialog.replace(() => <DialogSelectFile />), + onSelect: () => dialog.show(() => <DialogSelectFile />), }, // { // id: "theme.toggle", @@ -245,7 +245,7 @@ export default function Page() { category: "Model", keybind: "mod+'", slash: "model", - onSelect: () => dialog.replace(() => <DialogSelectModel />), + onSelect: () => dialog.show(() => <DialogSelectModel />), }, { id: "agent.cycle", @@ -320,7 +320,7 @@ export default function Page() { const handleKeyDown = (event: KeyboardEvent) => { if ((document.activeElement as HTMLElement)?.dataset?.component === "terminal") return - if (dialog.stack.length > 0) return + if (dialog.active) return if (event.key === "PageUp" || event.key === "PageDown") { const scrollContainer = document.querySelector('[data-slot="session-turn-content"]') as HTMLElement @@ -613,7 +613,7 @@ export default function Page() { icon="plus-small" variant="ghost" iconSize="large" - onClick={() => dialog.replace(() => <DialogSelectFile />)} + onClick={() => dialog.show(() => <DialogSelectFile />)} /> </Tooltip> </div> diff --git a/packages/ui/src/context/dialog.tsx b/packages/ui/src/context/dialog.tsx index b15d96991..71fc63806 100644 --- a/packages/ui/src/context/dialog.tsx +++ b/packages/ui/src/context/dialog.tsx @@ -1,9 +1,7 @@ import { createContext, createEffect, - createMemo, createSignal, - For, getOwner, Owner, ParentProps, @@ -13,73 +11,58 @@ import { type JSX, } from "solid-js" import { Dialog as Kobalte } from "@kobalte/core/dialog" -import { iife } from "@opencode-ai/util/iife" type DialogElement = () => JSX.Element const Context = createContext<ReturnType<typeof init>>() function init() { - const [store, setStore] = createSignal< - { - id: string - element: DialogElement - onClose?: () => void - owner: Owner - }[] - >([]) + const [active, setActive] = createSignal< + | { + id: string + element: DialogElement + onClose?: () => void + owner: Owner + } + | undefined + >() const result = { - get stack() { - return store() - }, - pop() { - const current = store().at(-1) - if (!current) return - current?.onClose?.() - setStore((stack) => { - stack.pop() - return [...stack] - }) + get active() { + return active() }, - replace(element: DialogElement, owner: Owner, onClose?: () => void) { - for (const item of store()) { - item.onClose?.() - } - const id = Math.random().toString(36) - setStore([ - { - id, - element: () => - runWithOwner(owner, () => ( - <Show when={result.stack.at(-1)?.id === id}> - <Kobalte - modal - defaultOpen - onOpenChange={(open) => { - if (!open) { - onClose?.() - result.pop() - } - }} - > - <Kobalte.Portal> - <Kobalte.Overlay data-component="dialog-overlay" /> - {element()} - </Kobalte.Portal> - </Kobalte> - </Show> - )), - onClose, - owner, - }, - ]) + close() { + active()?.onClose?.() + setActive(undefined) }, - clear() { - for (const item of store()) { - item.onClose?.() - } - setStore([]) + show(element: DialogElement, owner: Owner, onClose?: () => void) { + active()?.onClose?.() + const id = Math.random().toString(36).slice(2) + setActive({ + id, + element: () => + runWithOwner(owner, () => ( + <Show when={active()?.id === id}> + <Kobalte + modal + open={true} + onOpenChange={(open) => { + if (!open) { + console.log("closing") + result.close() + } + }} + > + <Kobalte.Portal> + <Kobalte.Overlay data-component="dialog-overlay" /> + {element()} + </Kobalte.Portal> + </Kobalte> + </Show> + )), + onClose, + owner, + }) }, } @@ -89,14 +72,12 @@ function init() { export function DialogProvider(props: ParentProps) { const ctx = init() createEffect(() => { - console.log("store", ctx.stack.length) + console.log("active", ctx.active) }) return ( <Context.Provider value={ctx}> {props.children} - <div data-component="dialog-stack"> - <For each={ctx.stack}>{(item) => <>{item.element()}</>}</For> - </div> + <div data-component="dialog-stack">{ctx.active?.element?.()}</div> </Context.Provider> ) } @@ -111,17 +92,14 @@ export function useDialog() { 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) + get active() { + return ctx.active }, - pop() { - ctx.pop() + show(element: DialogElement, onClose?: () => void) { + ctx.show(element, owner, onClose) }, - clear() { - ctx.clear() + close() { + ctx.close() }, } } |
