summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-01-01 20:04:46 -0600
committerAdam <[email protected]>2026-01-01 21:03:06 -0600
commitafc1825cf5610a9455d8ab22a946c91f00a6db97 (patch)
tree500e1a810f3cefa5cc897a46e38b8ce11a235615
parent6b4c433e14c2a423ae1e25151dd47ba4f0573001 (diff)
downloadopencode-afc1825cf5610a9455d8ab22a946c91f00a6db97.tar.gz
opencode-afc1825cf5610a9455d8ab22a946c91f00a6db97.zip
wip(app): progress
-rw-r--r--packages/app/src/components/session/index.ts1
-rw-r--r--packages/app/src/components/session/session-review-tab.tsx86
-rw-r--r--packages/app/src/pages/session.tsx95
3 files changed, 93 insertions, 89 deletions
diff --git a/packages/app/src/components/session/index.ts b/packages/app/src/components/session/index.ts
index 5e5c349d2..20124b6fd 100644
--- a/packages/app/src/components/session/index.ts
+++ b/packages/app/src/components/session/index.ts
@@ -1,6 +1,5 @@
export { SessionHeader } from "./session-header"
export { SessionContextTab } from "./session-context-tab"
-export { SessionReviewTab } from "./session-review-tab"
export { SortableTab, FileVisual } from "./session-sortable-tab"
export { SortableTerminalTab } from "./session-sortable-terminal-tab"
export { NewSessionView } from "./session-new-view"
diff --git a/packages/app/src/components/session/session-review-tab.tsx b/packages/app/src/components/session/session-review-tab.tsx
deleted file mode 100644
index c9656eb72..000000000
--- a/packages/app/src/components/session/session-review-tab.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-import { createEffect, on, onCleanup } from "solid-js"
-import { useLayout } from "@/context/layout"
-import { SessionReview } from "@opencode-ai/ui/session-review"
-import type { FileDiff } from "@opencode-ai/sdk/v2/client"
-
-interface SessionReviewTabProps {
- diffs: () => FileDiff[]
- view: () => ReturnType<ReturnType<typeof useLayout>["view"]>
- classes?: {
- root?: string
- header?: string
- container?: string
- }
-}
-
-export function SessionReviewTab(props: SessionReviewTabProps) {
- const layout = useLayout()
-
- let scroll: HTMLDivElement | undefined
- let frame: number | undefined
- let pending: { x: number; y: number } | undefined
-
- const restoreScroll = () => {
- const el = scroll
- if (!el) return
-
- const s = props.view().scroll("review")
- if (!s) return
-
- if (el.scrollTop !== s.y) el.scrollTop = s.y
- if (el.scrollLeft !== s.x) el.scrollLeft = s.x
- }
-
- const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => {
- pending = {
- x: event.currentTarget.scrollLeft,
- y: event.currentTarget.scrollTop,
- }
- if (frame !== undefined) return
-
- frame = requestAnimationFrame(() => {
- frame = undefined
-
- const next = pending
- pending = undefined
- if (!next) return
-
- props.view().setScroll("review", next)
- })
- }
-
- createEffect(
- on(
- () => props.diffs().length,
- () => {
- requestAnimationFrame(restoreScroll)
- },
- { defer: true },
- ),
- )
-
- onCleanup(() => {
- if (frame === undefined) return
- cancelAnimationFrame(frame)
- })
-
- return (
- <SessionReview
- scrollRef={(el) => {
- scroll = el
- restoreScroll()
- }}
- onScroll={handleScroll}
- open={props.view().review.open()}
- onOpenChange={props.view().review.setOpen}
- classes={{
- root: props.classes?.root ?? "pb-40",
- header: props.classes?.header ?? "px-6",
- container: props.classes?.container ?? "px-6",
- }}
- diffs={props.diffs()}
- diffStyle={layout.review.diffStyle()}
- onDiffStyleChange={layout.review.setDiffStyle}
- />
- )
-}
diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx
index bf1d06c1d..23e9a273f 100644
--- a/packages/app/src/pages/session.tsx
+++ b/packages/app/src/pages/session.tsx
@@ -15,6 +15,7 @@ import { Tabs } from "@opencode-ai/ui/tabs"
import { useCodeComponent } from "@opencode-ai/ui/context/code"
import { SessionTurn } from "@opencode-ai/ui/session-turn"
import { createAutoScroll } from "@opencode-ai/ui/hooks"
+import { SessionReview } from "@opencode-ai/ui/session-review"
import { SessionMessageRail } from "@opencode-ai/ui/session-message-rail"
import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter } from "@thisbeyond/solid-dnd"
@@ -31,6 +32,7 @@ import { DialogSelectMcp } from "@/components/dialog-select-mcp"
import { useCommand } from "@/context/command"
import { useNavigate, useParams } from "@solidjs/router"
import { UserMessage } from "@opencode-ai/sdk/v2"
+import type { FileDiff } from "@opencode-ai/sdk/v2/client"
import { useSDK } from "@/context/sdk"
import { usePrompt } from "@/context/prompt"
import { extractPromptFromParts } from "@/utils/prompt"
@@ -40,7 +42,6 @@ import { showToast } from "@opencode-ai/ui/toast"
import {
SessionHeader,
SessionContextTab,
- SessionReviewTab,
SortableTab,
FileVisual,
SortableTerminalTab,
@@ -53,6 +54,90 @@ function same<T>(a: readonly T[], b: readonly T[]) {
return a.every((x, i) => x === b[i])
}
+type DiffStyle = "unified" | "split"
+
+interface SessionReviewTabProps {
+ diffs: () => FileDiff[]
+ view: () => ReturnType<ReturnType<typeof useLayout>["view"]>
+ diffStyle: DiffStyle
+ onDiffStyleChange?: (style: DiffStyle) => void
+ classes?: {
+ root?: string
+ header?: string
+ container?: string
+ }
+}
+
+function SessionReviewTab(props: SessionReviewTabProps) {
+ let scroll: HTMLDivElement | undefined
+ let frame: number | undefined
+ let pending: { x: number; y: number } | undefined
+
+ const restoreScroll = () => {
+ const el = scroll
+ if (!el) return
+
+ const s = props.view().scroll("review")
+ if (!s) return
+
+ if (el.scrollTop !== s.y) el.scrollTop = s.y
+ if (el.scrollLeft !== s.x) el.scrollLeft = s.x
+ }
+
+ const handleScroll = (event: Event & { currentTarget: HTMLDivElement }) => {
+ pending = {
+ x: event.currentTarget.scrollLeft,
+ y: event.currentTarget.scrollTop,
+ }
+ if (frame !== undefined) return
+
+ frame = requestAnimationFrame(() => {
+ frame = undefined
+
+ const next = pending
+ pending = undefined
+ if (!next) return
+
+ props.view().setScroll("review", next)
+ })
+ }
+
+ createEffect(
+ on(
+ () => props.diffs().length,
+ () => {
+ requestAnimationFrame(restoreScroll)
+ },
+ { defer: true },
+ ),
+ )
+
+ onCleanup(() => {
+ if (frame === undefined) return
+ cancelAnimationFrame(frame)
+ })
+
+ return (
+ <SessionReview
+ scrollRef={(el) => {
+ scroll = el
+ restoreScroll()
+ }}
+ onScroll={handleScroll}
+ open={props.view().review.open()}
+ onOpenChange={props.view().review.setOpen}
+ classes={{
+ root: props.classes?.root ?? "pb-40",
+ header: props.classes?.header ?? "px-6",
+ container: props.classes?.container ?? "px-6",
+ }}
+ diffs={props.diffs()}
+ diffStyle={props.diffStyle}
+ onDiffStyleChange={props.onDiffStyleChange}
+ />
+ )
+}
+
export default function Page() {
const layout = useLayout()
const local = useLocal()
@@ -829,6 +914,7 @@ export default function Page() {
<SessionReviewTab
diffs={diffs}
view={view}
+ diffStyle="unified"
classes={{
root: "pb-32",
header: "px-4",
@@ -1007,7 +1093,12 @@ export default function Page() {
<Show when={reviewTab()}>
<Tabs.Content value="review" class="flex flex-col h-full overflow-hidden contain-strict">
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
- <SessionReviewTab diffs={diffs} view={view} />
+ <SessionReviewTab
+ diffs={diffs}
+ view={view}
+ diffStyle={layout.review.diffStyle()}
+ onDiffStyleChange={layout.review.setDiffStyle}
+ />
</div>
</Tabs.Content>
</Show>