feat(desktop): enhance Windows app resolution and UI loading states (#13084)
This commit is contained in:
@@ -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 [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
|
||||
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 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)
|
||||
|
||||
Reference in New Issue
Block a user