diff options
| author | adamelmore <[email protected]> | 2026-01-27 20:24:23 -0600 |
|---|---|---|
| committer | adamelmore <[email protected]> | 2026-01-27 20:24:27 -0600 |
| commit | d9741866c52472469c8858b83f44ad9c5a5f4925 (patch) | |
| tree | b9d168939286d44ae382880fa6601ff03a9a1b82 /packages | |
| parent | 898118bafbf8a12c0ef9d6673d36305472991906 (diff) | |
| download | opencode-d9741866c52472469c8858b83f44ad9c5a5f4925.tar.gz opencode-d9741866c52472469c8858b83f44ad9c5a5f4925.zip | |
fix(app): reintroduce review tab
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/app/src/components/prompt-input.tsx | 1 | ||||
| -rw-r--r-- | packages/app/src/context/layout.tsx | 13 | ||||
| -rw-r--r-- | packages/app/src/pages/session.tsx | 142 |
3 files changed, 108 insertions, 48 deletions
diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 9f038b6e8..d46ce095a 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/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 } diff --git a/packages/app/src/context/layout.tsx b/packages/app/src/context/layout.tsx index d30fd11cf..2ea5f0435 100644 --- a/packages/app/src/context/layout.tsx +++ b/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]) { diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index a845d3a65..7b4f31c50 100644 --- a/packages/app/src/pages/session.tsx +++ b/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> |
