fix(app): allow toggling file tree closed independently (#12293)

This commit is contained in:
Adam
2026-02-05 13:51:08 -06:00
committed by GitHub
parent c40ce47e92
commit 83646e0366
6 changed files with 185 additions and 23 deletions

View File

@@ -395,7 +395,7 @@ export default function Page() {
}
const isDesktop = createMediaQuery("(min-width: 768px)")
const centered = createMemo(() => isDesktop() && !layout.fileTree.opened())
const centered = createMemo(() => isDesktop() && !view().reviewPanel.opened())
function normalizeTab(tab: string) {
if (!tab.startsWith("file://")) return tab
@@ -1043,7 +1043,18 @@ export default function Page() {
description: "",
category: language.t("command.category.view"),
keybind: "mod+shift+r",
onSelect: () => layout.fileTree.toggle(),
onSelect: () => view().reviewPanel.toggle(),
},
{
id: "fileTree.toggle",
title: language.t("command.fileTree.toggle"),
description: "",
category: language.t("command.category.view"),
onSelect: () => {
const opening = !layout.fileTree.opened()
if (opening && !view().reviewPanel.opened()) view().reviewPanel.open()
layout.fileTree.toggle()
},
},
{
id: "terminal.new",
@@ -1409,10 +1420,11 @@ export default function Page() {
const openedTabs = createMemo(() =>
tabs()
.all()
.filter((tab) => tab !== "context"),
.filter((tab) => tab !== "context" && tab !== "review"),
)
const mobileChanges = createMemo(() => !isDesktop() && store.mobileTab === "changes")
const reviewTab = createMemo(() => isDesktop() && !layout.fileTree.opened())
const fileTreeTab = () => layout.fileTree.tab()
const setFileTreeTab = (value: "changes" | "all") => layout.fileTree.setTab(value)
@@ -1627,29 +1639,71 @@ export default function Page() {
const activeTab = createMemo(() => {
const active = tabs().active()
if (active === "context") return "context"
if (active === "review" && reviewTab()) return "review"
if (active && file.pathFromTab(active)) return normalizeTab(active)
const first = openedTabs()[0]
if (first) return first
if (contextOpen()) return "context"
if (reviewTab() && hasReview()) return "review"
return "empty"
})
createEffect(() => {
if (!layout.ready()) return
if (tabs().active()) return
if (openedTabs().length === 0 && !contextOpen()) return
if (openedTabs().length === 0 && !contextOpen() && !(reviewTab() && hasReview())) return
const next = activeTab()
if (next === "empty") return
tabs().setActive(next)
})
createEffect(
on(
() => layout.fileTree.opened(),
(opened, prev) => {
if (prev === undefined) return
if (!isDesktop()) return
if (opened) {
const active = tabs().active()
const tab = active === "review" || (!active && hasReview()) ? "changes" : "all"
layout.fileTree.setTab(tab)
return
}
if (fileTreeTab() !== "changes") return
tabs().setActive("review")
},
{ defer: true },
),
)
createEffect(() => {
if (!isDesktop()) return
if (!layout.fileTree.opened()) return
if (fileTreeTab() !== "all") return
const active = tabs().active()
if (active && active !== "review") return
const first = openedTabs()[0]
if (first) {
tabs().setActive(first)
return
}
if (contextOpen()) tabs().setActive("context")
})
createEffect(() => {
const id = params.id
if (!id) return
const wants = isDesktop() ? layout.fileTree.opened() : store.mobileTab === "changes"
const wants = isDesktop()
? view().reviewPanel.opened() && (layout.fileTree.opened() || activeTab() === "review")
: store.mobileTab === "changes"
if (!wants) return
if (sync.data.session_diff[id] !== undefined) return
if (sync.status === "loading") return
@@ -1661,6 +1715,7 @@ export default function Page() {
createEffect(() => {
const dir = sdk.directory
if (!isDesktop()) return
if (!view().reviewPanel.opened()) return
if (!layout.fileTree.opened()) return
if (sync.status === "loading") return
@@ -2195,10 +2250,10 @@ export default function Page() {
classList={{
"@container relative shrink-0 flex flex-col min-h-0 h-full bg-background-stronger": true,
"flex-1 pt-2 md:pt-3": true,
"md:flex-none": layout.fileTree.opened(),
"md:flex-none": view().reviewPanel.opened(),
}}
style={{
width: isDesktop() && layout.fileTree.opened() ? `${layout.session.width()}px` : "100%",
width: isDesktop() && view().reviewPanel.opened() ? `${layout.session.width()}px` : "100%",
"--prompt-height": store.promptHeight ? `${store.promptHeight}px` : undefined,
}}
>
@@ -2711,7 +2766,7 @@ export default function Page() {
</div>
</div>
<Show when={isDesktop() && layout.fileTree.opened()}>
<Show when={isDesktop() && view().reviewPanel.opened()}>
<ResizeHandle
direction="horizontal"
size={layout.session.width()}
@@ -2723,7 +2778,7 @@ export default function Page() {
</div>
{/* Desktop side panel - hidden on mobile */}
<Show when={isDesktop() && layout.fileTree.opened()}>
<Show when={isDesktop() && view().reviewPanel.opened()}>
<aside
id="review-panel"
aria-label={language.t("session.panel.reviewAndFiles")}
@@ -2731,7 +2786,7 @@ export default function Page() {
>
<div class="flex-1 min-w-0 h-full">
<Show
when={fileTreeTab() === "changes"}
when={layout.fileTree.opened() && fileTreeTab() === "changes"}
fallback={
<DragDropProvider
onDragStart={handleDragStart}
@@ -2799,6 +2854,18 @@ export default function Page() {
})
}}
>
<Show when={reviewTab()}>
<Tabs.Trigger value="review" classes={{ button: "!pl-6" }}>
<div class="flex items-center gap-1.5">
<div>{language.t("session.tab.review")}</div>
<Show when={hasReview()}>
<div class="text-12-medium text-text-strong h-4 px-2 flex flex-col items-center justify-center rounded-full bg-surface-base">
{reviewCount()}
</div>
</Show>
</div>
</Tabs.Trigger>
</Show>
<Show when={contextOpen()}>
<Tabs.Trigger
value="context"
@@ -2847,6 +2914,12 @@ export default function Page() {
</Tabs.List>
</div>
<Show when={reviewTab()}>
<Tabs.Content value="review" class="flex flex-col h-full overflow-hidden contain-strict">
<Show when={activeTab() === "review"}>{reviewPanel()}</Show>
</Tabs.Content>
</Show>
<Tabs.Content value="empty" class="flex flex-col h-full overflow-hidden contain-strict">
<Show when={activeTab() === "empty"}>
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">