Explorar el Código

fix(app): reintroduce review tab

adamelmore hace 3 semanas
padre
commit
d9741866c5

+ 1 - 0
packages/app/src/components/prompt-input.tsx

@@ -190,6 +190,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
     const wantsReview = item.commentOrigin === "review" || (item.commentOrigin !== "file" && commentInReview(item.path))
     if (wantsReview) {
       layout.fileTree.setTab("changes")
+      if (!layout.fileTree.opened()) tabs().open("review")
       requestAnimationFrame(() => comments.setFocus(focus))
       return
     }

+ 10 - 3
packages/app/src/context/layout.tsx

@@ -624,11 +624,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
         const tabs = createMemo(() => store.sessionTabs[key()] ?? { all: [] })
         return {
           tabs,
-          active: createMemo(() => (tabs().active === "review" ? undefined : tabs().active)),
+          active: createMemo(() => tabs().active),
           all: createMemo(() => tabs().all.filter((tab) => tab !== "review")),
           setActive(tab: string | undefined) {
             const session = key()
-            if (tab === "review") return
             if (!store.sessionTabs[session]) {
               setStore("sessionTabs", session, { all: [], active: tab })
             } else {
@@ -645,10 +644,18 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
             }
           },
           async open(tab: string) {
-            if (tab === "review") return
             const session = key()
             const current = store.sessionTabs[session] ?? { all: [] }
 
+            if (tab === "review") {
+              if (!store.sessionTabs[session]) {
+                setStore("sessionTabs", session, { all: current.all, active: tab })
+                return
+              }
+              setStore("sessionTabs", session, "active", tab)
+              return
+            }
+
             if (tab === "context") {
               const all = [tab, ...current.all.filter((x) => x !== tab)]
               if (!store.sessionTabs[session]) {

+ 97 - 45
packages/app/src/pages/session.tsx

@@ -23,6 +23,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
 import { Button } from "@opencode-ai/ui/button"
 import { Icon } from "@opencode-ai/ui/icon"
 import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
+import { DiffChanges } from "@opencode-ai/ui/diff-changes"
 import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
 import { Tabs } from "@opencode-ai/ui/tabs"
 import { useCodeComponent } from "@opencode-ai/ui/context/code"
@@ -1096,11 +1097,12 @@ export default function Page() {
     }, 0)
   }
 
+  const reviewTab = createMemo(() => isDesktop() && !layout.fileTree.opened())
   const contextOpen = createMemo(() => tabs().active() === "context" || tabs().all().includes("context"))
   const openedTabs = createMemo(() =>
     tabs()
       .all()
-      .filter((tab) => tab !== "context"),
+      .filter((tab) => tab !== "context" && tab !== "review"),
   )
 
   const mobileChanges = createMemo(() => !isDesktop() && store.mobileTab === "changes")
@@ -1126,6 +1128,46 @@ export default function Page() {
     setFileTreeTab("all")
   }
 
+  const reviewPanel = () => (
+    <div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict">
+      <div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
+        <Switch>
+          <Match when={hasReview()}>
+            <Show
+              when={diffsReady()}
+              fallback={<div class="px-6 py-4 text-text-weak">{language.t("session.review.loadingChanges")}</div>}
+            >
+              <SessionReviewTab
+                diffs={diffs}
+                view={view}
+                diffStyle={layout.review.diffStyle()}
+                onDiffStyleChange={layout.review.setDiffStyle}
+                onScrollRef={setReviewScroll}
+                focusedFile={activeDiff()}
+                onLineComment={(comment) => addCommentToContext({ ...comment, origin: "review" })}
+                comments={comments.all()}
+                focusedComment={comments.focus()}
+                onFocusedCommentChange={comments.setFocus}
+                onViewFile={(path) => {
+                  showAllFiles()
+                  const value = file.tab(path)
+                  tabs().open(value)
+                  file.load(path)
+                }}
+              />
+            </Show>
+          </Match>
+          <Match when={true}>
+            <div class="h-full px-6 pb-30 flex flex-col items-center justify-center text-center gap-6">
+              <Mark class="w-14 opacity-10" />
+              <div class="text-14-regular text-text-weak max-w-56">{language.t("session.review.empty")}</div>
+            </div>
+          </Match>
+        </Switch>
+      </div>
+    </div>
+  )
+
   createEffect(
     on(
       () => tabs().active(),
@@ -1229,29 +1271,56 @@ 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(() => {
     const id = params.id
     if (!id) return
 
-    const wants = isDesktop() ? fileTreeTab() === "changes" : store.mobileTab === "changes"
+    const wants = isDesktop()
+      ? layout.fileTree.opened()
+        ? fileTreeTab() === "changes"
+        : activeTab() === "review"
+      : store.mobileTab === "changes"
     if (!wants) return
     if (sync.data.session_diff[id] !== undefined) return
     if (sync.status === "loading") return
@@ -2114,7 +2183,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}
@@ -2127,6 +2196,23 @@ export default function Page() {
                     <Tabs value={activeTab()} onChange={openTab}>
                       <div class="sticky top-0 shrink-0 flex">
                         <Tabs.List>
+                          <Show when={reviewTab()}>
+                            <Tabs.Trigger value="review" classes={{ button: "!pl-6" }}>
+                              <div class="flex items-center gap-3">
+                                <Show when={hasReview() && diffsReady()}>
+                                  <DiffChanges changes={diffs()} variant="bars" />
+                                </Show>
+                                <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>
+                              </div>
+                            </Tabs.Trigger>
+                          </Show>
                           <Show when={contextOpen()}>
                             <Tabs.Trigger
                               value="context"
@@ -2175,6 +2261,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">
@@ -2710,47 +2802,7 @@ export default function Page() {
                   </DragDropProvider>
                 }
               >
-                <div class="flex flex-col h-full overflow-hidden bg-background-stronger contain-strict">
-                  <div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
-                    <Switch>
-                      <Match when={hasReview()}>
-                        <Show
-                          when={diffsReady()}
-                          fallback={
-                            <div class="px-6 py-4 text-text-weak">{language.t("session.review.loadingChanges")}</div>
-                          }
-                        >
-                          <SessionReviewTab
-                            diffs={diffs}
-                            view={view}
-                            diffStyle={layout.review.diffStyle()}
-                            onDiffStyleChange={layout.review.setDiffStyle}
-                            onScrollRef={setReviewScroll}
-                            focusedFile={activeDiff()}
-                            onLineComment={(comment) => addCommentToContext({ ...comment, origin: "review" })}
-                            comments={comments.all()}
-                            focusedComment={comments.focus()}
-                            onFocusedCommentChange={comments.setFocus}
-                            onViewFile={(path) => {
-                              showAllFiles()
-                              const value = file.tab(path)
-                              tabs().open(value)
-                              file.load(path)
-                            }}
-                          />
-                        </Show>
-                      </Match>
-                      <Match when={true}>
-                        <div class="h-full px-6 pb-30 flex flex-col items-center justify-center text-center gap-6">
-                          <Mark class="w-14 opacity-10" />
-                          <div class="text-14-regular text-text-weak max-w-56">
-                            {language.t("session.review.empty")}
-                          </div>
-                        </div>
-                      </Match>
-                    </Switch>
-                  </div>
-                </div>
+                {reviewPanel()}
               </Show>
             </div>