import { For, Show, Suspense, createMemo, lazy, 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 DiffToolbar from "../components/DiffToolbar" import SplitFilePanel from "../components/SplitFilePanel" import type { DiffContextMode, DiffViewMode, DiffWordWrapMode } from "../types" const LazyMonacoDiffViewer = lazy(() => import("../../../../file-viewer/monaco-diff-viewer").then((module) => ({ default: module.MonacoDiffViewer })), ) 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 diffWordWrapMode: Accessor onViewModeChange: (mode: DiffViewMode) => void onContextModeChange: (mode: DiffContextMode) => void onWordWrapModeChange: (mode: DiffWordWrapMode) => 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 sessionId = createMemo(() => props.activeSessionId()) const hasSession = createMemo(() => Boolean(sessionId() && sessionId() !== "info")) const entries = createMemo(() => (hasSession() ? props.entries() : null)) const sorted = createMemo(() => { const list = entries() if (!Array.isArray(list)) return [] return [...list].sort((a, b) => String(a.path || "").localeCompare(String(b.path || ""))) }) const totals = createMemo(() => { return 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 = createMemo(() => sorted().filter((item) => item && item.status !== "deleted")) const selectedEntry = createMemo(() => { const list = sorted() const selectedPath = props.selectedPath() const fallbackPath = props.mostChangedPath() const found = list.find((item) => item.path === selectedPath) || (fallbackPath ? list.find((item) => item.path === fallbackPath) : undefined) return found ?? null }) const emptyViewerMessage = createMemo(() => { if (!hasSession()) return props.t("instanceShell.gitChanges.noSessionSelected") const currentEntries = entries() if (currentEntries === null) return props.t("instanceShell.gitChanges.loading") if (nonDeleted().length === 0) return props.t("instanceShell.gitChanges.empty") return props.t("instanceShell.filesShell.viewerEmpty") }) const renderContent = (): JSX.Element => { const totalsValue = totals() const selected = selectedEntry() const sortedList = sorted() const nonDeletedList = nonDeleted() const renderViewer = () => (
{emptyViewerMessage()}
} > {(file) => ( {props.t("instanceInfo.loading")}
} > )} } > {(err) => (
{err()}
)} } >
{props.t("instanceInfo.loading")}
) const renderEmptyList = () =>
{emptyViewerMessage()}
const renderListPanel = () => ( 0} fallback={renderEmptyList()}> {(item) => (
{ props.onOpenFile(item.path) }} >
{item.path}
{props.t("instanceShell.gitChanges.deleted")} <> +{item.added} -{item.removed}
)}
) const renderListOverlay = () => ( 0} fallback={renderEmptyList()}> {(item) => (
props.onOpenFile(item.path)} title={item.path} >
{item.path}
{props.t("instanceShell.gitChanges.deleted")} <> +{item.added} -{item.removed}
)}
) return ( {selected?.path || props.t("instanceShell.rightPanel.tabs.gitChanges")}
+{totalsValue.additions} -{totalsValue.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={props.t("instanceShell.rightPanel.tabs.gitChanges")} /> ) } return <>{renderContent()} } export default GitChangesTab