import { For, Show, type Accessor, type Component, type JSX } from "solid-js" import type { File as GitFileStatus } from "@opencode-ai/sdk/v2/client" import { RefreshCw } from "lucide-solid" import { MonacoDiffViewer } from "../../../../file-viewer/monaco-diff-viewer" import DiffToolbar from "../components/DiffToolbar" import SplitFilePanel from "../components/SplitFilePanel" import type { DiffContextMode, DiffViewMode } from "../types" interface GitChangesTabProps { t: (key: string, vars?: Record) => string activeSessionId: Accessor entries: Accessor statusLoading: Accessor statusError: Accessor selectedPath: Accessor selectedLoading: Accessor selectedError: Accessor selectedBefore: Accessor selectedAfter: Accessor mostChangedPath: Accessor scopeKey: Accessor diffViewMode: Accessor diffContextMode: Accessor onViewModeChange: (mode: DiffViewMode) => void onContextModeChange: (mode: DiffContextMode) => void onOpenFile: (path: string) => void onRefresh: () => void listOpen: Accessor onToggleList: () => void splitWidth: Accessor onResizeMouseDown: (event: MouseEvent) => void onResizeTouchStart: (event: TouchEvent) => void isPhoneLayout: Accessor } const GitChangesTab: Component = (props) => { const renderContent = (): JSX.Element => { const sessionId = props.activeSessionId() const hasSession = Boolean(sessionId && sessionId !== "info") const entries = hasSession ? props.entries() : null const sorted = Array.isArray(entries) ? [...entries].sort((a, b) => String(a.path || "").localeCompare(String(b.path || ""))) : [] const totals = sorted.reduce( (acc, item) => { acc.additions += typeof item.added === "number" ? item.added : 0 acc.deletions += typeof item.removed === "number" ? item.removed : 0 return acc }, { additions: 0, deletions: 0 }, ) const nonDeleted = sorted.filter((item) => item && item.status !== "deleted") const emptyViewerMessage = () => { if (!hasSession) return "Select a session to view changes." if (entries === null) return "Loading git changes…" if (nonDeleted.length === 0) return "No git changes yet." return "No file selected." } const selectedPath = props.selectedPath() const fallbackPath = props.mostChangedPath() const selectedEntry = sorted.find((item) => item.path === selectedPath) || (fallbackPath ? sorted.find((item) => item.path === fallbackPath) : null) const renderViewer = () => (
{emptyViewerMessage()}
} > {(file) => ( )} } > {(err) => (
{err()}
)} } >
Loading…
) const renderEmptyList = () =>
{emptyViewerMessage()}
const renderListPanel = () => ( 0} fallback={renderEmptyList()}> {(item) => (
{ props.onOpenFile(item.path) }} >
{item.path}
deleted <> +{item.added} -{item.removed}
)}
) const renderListOverlay = () => ( 0} fallback={renderEmptyList()}> {(item) => (
props.onOpenFile(item.path)} title={item.path} >
{item.path}
deleted <> +{item.added} -{item.removed}
)}
) return ( {selectedEntry?.path || "Git Changes"}
+{totals.additions} -{totals.deletions} {(err) => {err()}}
} list={{ panel: renderListPanel, overlay: renderListOverlay }} viewer={renderViewer()} listOpen={props.listOpen()} onToggleList={props.onToggleList} splitWidth={props.splitWidth()} onResizeMouseDown={props.onResizeMouseDown} onResizeTouchStart={props.onResizeTouchStart} isPhoneLayout={props.isPhoneLayout()} overlayAriaLabel="Git Changes" /> ) } return <>{renderContent()} } export default GitChangesTab