summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-05 13:51:08 -0600
committerGitHub <[email protected]>2026-02-05 13:51:08 -0600
commit83646e0366c47a3bccb5135d40628176a6776f33 (patch)
treecd8525d1a684a659e31a4413301577fac3f8fb63 /packages/app/src/pages
parentc40ce47e92befbe4cb27735e4d870f540e75b646 (diff)
downloadopencode-83646e0366c47a3bccb5135d40628176a6776f33.tar.gz
opencode-83646e0366c47a3bccb5135d40628176a6776f33.zip
fix(app): allow toggling file tree closed independently (#12293)
Diffstat (limited to 'packages/app/src/pages')
-rw-r--r--packages/app/src/pages/session.tsx93
1 files changed, 83 insertions, 10 deletions
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index 0bdf5f7f3..b0b955ed1 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -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">