diff options
| author | Filip <[email protected]> | 2026-02-11 11:40:52 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-02-11 04:40:52 -0600 |
| commit | cf7a1b8d80581ebee7a968602cec2e0561e08cdd (patch) | |
| tree | 576ef572ee75de8c488300ff52241d98b0d3efc7 /packages/app/src/components/session | |
| parent | 5ba4c0e024359dd1817bb5513982ee8cbfb24001 (diff) | |
| download | opencode-cf7a1b8d80581ebee7a968602cec2e0561e08cdd.tar.gz opencode-cf7a1b8d80581ebee7a968602cec2e0561e08cdd.zip | |
feat(desktop): enhance Windows app resolution and UI loading states (#13084)
Diffstat (limited to 'packages/app/src/components/session')
| -rw-r--r-- | packages/app/src/components/session/session-header.tsx | 59 |
1 files changed, 42 insertions, 17 deletions
diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 94b843666..3bf3c08e9 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -1,4 +1,4 @@ -import { createEffect, createMemo, onCleanup, Show } from "solid-js" +import { createEffect, createMemo, createResource, onCleanup, Show } from "solid-js" import { createStore } from "solid-js/store" import { Portal } from "solid-js/web" import { useParams } from "@solidjs/router" @@ -18,6 +18,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button" import { Button } from "@opencode-ai/ui/button" import { AppIcon } from "@opencode-ai/ui/app-icon" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" +import { Spinner } from "@opencode-ai/ui/spinner" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { Popover } from "@opencode-ai/ui/popover" import { TextField } from "@opencode-ai/ui/text-field" @@ -167,6 +168,7 @@ export function SessionHeader() { const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp })) const [menu, setMenu] = createStore({ open: false }) + const [openRequest, setOpenRequest] = createStore({ app: undefined as OpenApp | undefined, version: 0 }) const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal()) const current = createMemo(() => options().find((o) => o.id === prefs.app) ?? options()[0]) @@ -179,20 +181,32 @@ export function SessionHeader() { setPrefs("app", options()[0]?.id ?? "finder") }) - const openDir = (app: OpenApp) => { - const directory = projectDirectory() - if (!directory) return - if (!canOpen()) return - - const item = options().find((o) => o.id === app) - const openWith = item && "openWith" in item ? item.openWith : undefined - Promise.resolve(platform.openPath?.(directory, openWith)).catch((err: unknown) => { - showToast({ - variant: "error", - title: language.t("common.requestFailed"), - description: err instanceof Error ? err.message : String(err), - }) + const [openTask] = createResource( + () => openRequest.app && openRequest.version, + async () => { + const app = openRequest.app + const directory = projectDirectory() + if (!app || !directory || !canOpen()) return + + const item = options().find((o) => o.id === app) + const openWith = item && "openWith" in item ? item.openWith : undefined + await platform.openPath?.(directory, openWith) + }, + ) + + createEffect(() => { + const err = openTask.error + if (!err) return + showToast({ + variant: "error", + title: language.t("common.requestFailed"), + description: err instanceof Error ? err.message : String(err), }) + }) + + const openDir = (app: OpenApp) => { + if (openTask.loading) return + setOpenRequest({ app, version: openRequest.version + 1 }) } const copyPath = () => { @@ -346,12 +360,18 @@ export function SessionHeader() { <div class="flex h-[24px] box-border items-center rounded-md border border-border-base bg-surface-panel overflow-hidden"> <Button variant="ghost" - class="rounded-none h-full py-0 pr-3 pl-2 gap-1.5 border-none shadow-none" + class="rounded-none h-full py-0 pr-3 pl-2 gap-1.5 border-none shadow-none disabled:!cursor-default" + classList={{ + "bg-surface-raised-base-active": openTask.loading, + }} onClick={() => openDir(current().id)} + disabled={openTask.loading} aria-label={language.t("session.header.open.ariaLabel", { app: current().label })} > <div class="flex size-5 shrink-0 items-center justify-center"> - <AppIcon id={current().icon} class="size-4" /> + <Show when={openTask.loading} fallback={<AppIcon id={current().icon} class="size-4" />}> + <Spinner class="size-3.5 text-icon-base" /> + </Show> </div> <span class="text-12-regular text-text-strong">Open</span> </Button> @@ -366,7 +386,11 @@ export function SessionHeader() { as={IconButton} icon="chevron-down" variant="ghost" - class="rounded-none h-full w-[24px] p-0 border-none shadow-none data-[expanded]:bg-surface-raised-base-active" + disabled={openTask.loading} + class="rounded-none h-full w-[24px] p-0 border-none shadow-none data-[expanded]:bg-surface-raised-base-active disabled:!cursor-default" + classList={{ + "bg-surface-raised-base-active": openTask.loading, + }} aria-label={language.t("session.header.open.menu")} /> <DropdownMenu.Portal> @@ -383,6 +407,7 @@ export function SessionHeader() { {options().map((o) => ( <DropdownMenu.RadioItem value={o.id} + disabled={openTask.loading} onSelect={() => { setMenu("open", false) openDir(o.id) |
