Compare commits

...

3 Commits

Author SHA1 Message Date
Aiden Cline
32258aa028 Revert "feat(tui): restore footer to session view (#12245)"
This reverts commit 449c5b44b7.
2026-02-09 10:02:30 -06:00
Adam
5be1202eea chore: cleanup 2026-02-09 09:58:55 -06:00
Joseph Campuzano
373b2270e7 fix(app): make keyboard focus visible in settings (#12612) 2026-02-09 09:12:06 -06:00
7 changed files with 60 additions and 35 deletions

View File

@@ -112,21 +112,35 @@ export function SessionHeader() {
const [exists, setExists] = createStore<Partial<Record<OpenApp, boolean>>>({ finder: true })
const apps = createMemo(() => {
if (os() === "macos") return MAC_APPS
if (os() === "windows") return WINDOWS_APPS
return LINUX_APPS
})
const fileManager = createMemo(() => {
if (os() === "macos") return { label: "Finder", icon: "finder" as const }
if (os() === "windows") return { label: "File Explorer", icon: "file-explorer" as const }
return { label: "File Manager", icon: "finder" as const }
})
createEffect(() => {
if (platform.platform !== "desktop") return
if (!platform.checkAppExists) return
const list = os()
const apps = list === "macos" ? MAC_APPS : list === "windows" ? WINDOWS_APPS : list === "linux" ? LINUX_APPS : []
if (apps.length === 0) return
const list = apps()
setExists(Object.fromEntries(list.map((app) => [app.id, undefined])) as Partial<Record<OpenApp, boolean>>)
void Promise.all(
apps.map((app) =>
Promise.resolve(platform.checkAppExists?.(app.openWith)).then((value) => {
const ok = Boolean(value)
console.debug(`[session-header] App "${app.label}" (${app.openWith}): ${ok ? "exists" : "does not exist"}`)
return [app.id, ok] as const
}),
list.map((app) =>
Promise.resolve(platform.checkAppExists?.(app.openWith))
.then((value) => Boolean(value))
.catch(() => false)
.then((ok) => {
console.debug(`[session-header] App "${app.label}" (${app.openWith}): ${ok ? "exists" : "does not exist"}`)
return [app.id, ok] as const
}),
),
).then((entries) => {
setExists(Object.fromEntries(entries) as Partial<Record<OpenApp, boolean>>)
@@ -134,23 +148,23 @@ export function SessionHeader() {
})
const options = createMemo(() => {
if (os() === "macos") {
return [{ id: "finder", label: "Finder", icon: "finder" }, ...MAC_APPS.filter((app) => exists[app.id])] as const
}
if (os() === "windows") {
return [
{ id: "finder", label: "File Explorer", icon: "file-explorer" },
...WINDOWS_APPS.filter((app) => exists[app.id]),
] as const
}
return [
{ id: "finder", label: "File Manager", icon: "finder" },
...LINUX_APPS.filter((app) => exists[app.id]),
{ id: "finder", label: fileManager().label, icon: fileManager().icon },
...apps().filter((app) => exists[app.id]),
] as const
})
type OpenIcon = OpenApp | "file-explorer"
const base = new Set<OpenIcon>(["finder", "vscode", "cursor", "zed"])
const size = (id: OpenIcon) => (base.has(id) ? "size-4" : "size-[19px]")
const checksReady = createMemo(() => {
if (platform.platform !== "desktop") return true
if (!platform.checkAppExists) return true
const list = apps()
return list.every((app) => exists[app.id] !== undefined)
})
const [prefs, setPrefs] = persisted(Persist.global("open.app"), createStore({ app: "finder" as OpenApp }))
const canOpen = createMemo(() => platform.platform === "desktop" && !!platform.openPath && server.isLocal())
@@ -158,6 +172,7 @@ export function SessionHeader() {
createEffect(() => {
if (platform.platform !== "desktop") return
if (!checksReady()) return
const value = prefs.app
if (options().some((o) => o.id === value)) return
setPrefs("app", options()[0]?.id ?? "finder")
@@ -334,11 +349,13 @@ export function SessionHeader() {
onClick={() => openDir(current().id)}
aria-label={language.t("session.header.open.ariaLabel", { app: current().label })}
>
<AppIcon id={current().icon} class="size-4" />
<div class="flex size-5 shrink-0 items-center justify-center">
<AppIcon id={current().icon} class="size-4" />
</div>
<span class="text-12-regular text-text-strong">Open</span>
</Button>
<div class="self-stretch w-px bg-border-base/70" />
<DropdownMenu>
<DropdownMenu gutter={6} placement="bottom-end">
<DropdownMenu.Trigger
as={IconButton}
icon="chevron-down"
@@ -347,7 +364,7 @@ export function SessionHeader() {
aria-label={language.t("session.header.open.menu")}
/>
<DropdownMenu.Portal>
<DropdownMenu.Content placement="bottom-end" gutter={6}>
<DropdownMenu.Content>
<DropdownMenu.Group>
<DropdownMenu.GroupLabel>{language.t("session.header.openIn")}</DropdownMenu.GroupLabel>
<DropdownMenu.RadioGroup
@@ -359,7 +376,9 @@ export function SessionHeader() {
>
{options().map((o) => (
<DropdownMenu.RadioItem value={o.id} onSelect={() => openDir(o.id)}>
<AppIcon id={o.icon} class="size-5" />
<div class="flex size-5 shrink-0 items-center justify-center">
<AppIcon id={o.icon} class={size(o.icon)} />
</div>
<DropdownMenu.ItemLabel>{o.label}</DropdownMenu.ItemLabel>
<DropdownMenu.ItemIndicator>
<Icon name="check-small" size="small" class="text-icon-weak" />
@@ -370,7 +389,9 @@ export function SessionHeader() {
</DropdownMenu.Group>
<DropdownMenu.Separator />
<DropdownMenu.Item onSelect={copyPath}>
<Icon name="copy" size="small" class="text-icon-weak" />
<div class="flex size-5 shrink-0 items-center justify-center">
<Icon name="copy" size="small" class="text-icon-weak" />
</div>
<DropdownMenu.ItemLabel>
{language.t("session.header.open.copyPath")}
</DropdownMenu.ItemLabel>

View File

@@ -1099,9 +1099,6 @@ export function Session() {
sessionID={route.sessionID}
/>
</box>
<Show when={!sidebarVisible() || !wide()}>
<Footer />
</Show>
</Show>
<Toast />
</box>

View File

@@ -79,9 +79,9 @@
background-color: var(--surface-hover);
}
&:focus-within:not([data-readonly]) [data-slot="checkbox-checkbox-control"] {
&:not([data-readonly]) [data-slot="checkbox-checkbox-input"]:focus-visible + [data-slot="checkbox-checkbox-control"] {
border-color: var(--border-focus);
box-shadow: 0 0 0 2px var(--surface-focus);
box-shadow: var(--shadow-xs-border-focus);
}
&[data-checked] [data-slot="checkbox-checkbox-control"],

View File

@@ -32,6 +32,7 @@
/* } */
&:focus-visible {
outline: none;
background-color: var(--surface-raised-base-hover);
}
&[data-disabled] {
cursor: not-allowed;
@@ -70,6 +71,7 @@
/* } */
&:focus-visible {
outline: none;
background-color: var(--surface-raised-base-hover);
}
&[data-disabled] {
cursor: not-allowed;

View File

@@ -82,7 +82,7 @@
box-shadow: var(--shadow-xs-border-base);
}
&:not([data-expanded]):focus {
&:not([data-expanded]):not(:focus-visible):focus {
background-color: transparent;
box-shadow: none;
}

View File

@@ -86,9 +86,9 @@
background-color: var(--surface-hover);
}
&:focus-within:not([data-readonly]) [data-slot="switch-control"] {
&:not([data-readonly]) [data-slot="switch-input"]:focus-visible ~ [data-slot="switch-control"] {
border-color: var(--border-focus);
box-shadow: 0 0 0 2px var(--surface-focus);
box-shadow: var(--shadow-xs-border-focus);
}
&[data-checked] [data-slot="switch-control"] {

View File

@@ -429,6 +429,11 @@
background-color: var(--surface-raised-base-hover);
}
&:has([data-slot="tabs-trigger"]:focus-visible) {
background-color: var(--surface-raised-base-hover);
box-shadow: var(--shadow-xs-border-focus);
}
&:has([data-selected]) {
background-color: var(--surface-raised-base-active);
color: var(--text-strong);