Skip to content

Commit cf7a1b8

Browse files
authored
feat(desktop): enhance Windows app resolution and UI loading states (anomalyco#13084)
1 parent 5ba4c0e commit cf7a1b8

File tree

2 files changed

+350
-87
lines changed

2 files changed

+350
-87
lines changed

packages/app/src/components/session/session-header.tsx

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createEffect, createMemo, onCleanup, Show } from "solid-js"
1+
import { createEffect, createMemo, createResource, onCleanup, Show } from "solid-js"
22
import { createStore } from "solid-js/store"
33
import { Portal } from "solid-js/web"
44
import { useParams } from "@solidjs/router"
@@ -18,6 +18,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
1818
import { Button } from "@opencode-ai/ui/button"
1919
import { AppIcon } from "@opencode-ai/ui/app-icon"
2020
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
21+
import { Spinner } from "@opencode-ai/ui/spinner"
2122
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
2223
import { Popover } from "@opencode-ai/ui/popover"
2324
import { TextField } from "@opencode-ai/ui/text-field"
@@ -167,6 +168,7 @@ export function SessionHeader() {
167168

168169
const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp }))
169170
const [menu, setMenu] = createStore({ open: false })
171+
const [openRequest, setOpenRequest] = createStore({ app: undefined as OpenApp | undefined, version: 0 })
170172

171173
const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal())
172174
const current = createMemo(() => options().find((o) => o.id === prefs.app) ?? options()[0])
@@ -179,20 +181,32 @@ export function SessionHeader() {
179181
setPrefs("app", options()[0]?.id ?? "finder")
180182
})
181183

182-
const openDir = (app: OpenApp) => {
183-
const directory = projectDirectory()
184-
if (!directory) return
185-
if (!canOpen()) return
186-
187-
const item = options().find((o) => o.id === app)
188-
const openWith = item && "openWith" in item ? item.openWith : undefined
189-
Promise.resolve(platform.openPath?.(directory, openWith)).catch((err: unknown) => {
190-
showToast({
191-
variant: "error",
192-
title: language.t("common.requestFailed"),
193-
description: err instanceof Error ? err.message : String(err),
194-
})
184+
const [openTask] = createResource(
185+
() => openRequest.app && openRequest.version,
186+
async () => {
187+
const app = openRequest.app
188+
const directory = projectDirectory()
189+
if (!app || !directory || !canOpen()) return
190+
191+
const item = options().find((o) => o.id === app)
192+
const openWith = item && "openWith" in item ? item.openWith : undefined
193+
await platform.openPath?.(directory, openWith)
194+
},
195+
)
196+
197+
createEffect(() => {
198+
const err = openTask.error
199+
if (!err) return
200+
showToast({
201+
variant: "error",
202+
title: language.t("common.requestFailed"),
203+
description: err instanceof Error ? err.message : String(err),
195204
})
205+
})
206+
207+
const openDir = (app: OpenApp) => {
208+
if (openTask.loading) return
209+
setOpenRequest({ app, version: openRequest.version + 1 })
196210
}
197211

198212
const copyPath = () => {
@@ -346,12 +360,18 @@ export function SessionHeader() {
346360
<div class="flex h-[24px] box-border items-center rounded-md border border-border-base bg-surface-panel overflow-hidden">
347361
<Button
348362
variant="ghost"
349-
class="rounded-none h-full py-0 pr-3 pl-2 gap-1.5 border-none shadow-none"
363+
class="rounded-none h-full py-0 pr-3 pl-2 gap-1.5 border-none shadow-none disabled:!cursor-default"
364+
classList={{
365+
"bg-surface-raised-base-active": openTask.loading,
366+
}}
350367
onClick={() => openDir(current().id)}
368+
disabled={openTask.loading}
351369
aria-label={language.t("session.header.open.ariaLabel", { app: current().label })}
352370
>
353371
<div class="flex size-5 shrink-0 items-center justify-center">
354-
<AppIcon id={current().icon} class="size-4" />
372+
<Show when={openTask.loading} fallback={<AppIcon id={current().icon} class="size-4" />}>
373+
<Spinner class="size-3.5 text-icon-base" />
374+
</Show>
355375
</div>
356376
<span class="text-12-regular text-text-strong">Open</span>
357377
</Button>
@@ -366,7 +386,11 @@ export function SessionHeader() {
366386
as={IconButton}
367387
icon="chevron-down"
368388
variant="ghost"
369-
class="rounded-none h-full w-[24px] p-0 border-none shadow-none data-[expanded]:bg-surface-raised-base-active"
389+
disabled={openTask.loading}
390+
class="rounded-none h-full w-[24px] p-0 border-none shadow-none data-[expanded]:bg-surface-raised-base-active disabled:!cursor-default"
391+
classList={{
392+
"bg-surface-raised-base-active": openTask.loading,
393+
}}
370394
aria-label={language.t("session.header.open.menu")}
371395
/>
372396
<DropdownMenu.Portal>
@@ -383,6 +407,7 @@ export function SessionHeader() {
383407
{options().map((o) => (
384408
<DropdownMenu.RadioItem
385409
value={o.id}
410+
disabled={openTask.loading}
386411
onSelect={() => {
387412
setMenu("open", false)
388413
openDir(o.id)

0 commit comments

Comments
 (0)