import { For, Show, Suspense, createMemo, lazy, type Accessor, type Component, type JSX } from "solid-js" 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 ChangesTabProps { t: (key: string, vars?: Record) => string instanceId: string activeSessionId: Accessor activeSessionDiffs: Accessor selectedFile: Accessor onSelectFile: (file: string, closeList: boolean) => void diffViewMode: Accessor diffContextMode: Accessor diffWordWrapMode: Accessor onViewModeChange: (mode: DiffViewMode) => void onContextModeChange: (mode: DiffContextMode) => void onWordWrapModeChange: (mode: DiffWordWrapMode) => void listOpen: Accessor onToggleList: () => void splitWidth: Accessor onResizeMouseDown: (event: MouseEvent) => void onResizeTouchStart: (event: TouchEvent) => void isPhoneLayout: Accessor } const ChangesTab: Component = (props) => { const sessionId = createMemo(() => props.activeSessionId()) const hasSession = createMemo(() => Boolean(sessionId() && sessionId() !== "info")) const diffs = createMemo(() => (hasSession() ? props.activeSessionDiffs() : null)) const sorted = createMemo(() => { const list = diffs() if (!Array.isArray(list)) return [] return [...list].sort((a, b) => String(a.file || "").localeCompare(String(b.file || ""))) }) const totals = createMemo(() => { return sorted().reduce( (acc, item) => { acc.additions += typeof item.additions === "number" ? item.additions : 0 acc.deletions += typeof item.deletions === "number" ? item.deletions : 0 return acc }, { additions: 0, deletions: 0 }, ) }) const mostChanged = createMemo(() => { const items = sorted() if (items.length === 0) return null return items.reduce((best, item) => { const bestAdd = typeof (best as any)?.additions === "number" ? (best as any).additions : 0 const bestDel = typeof (best as any)?.deletions === "number" ? (best as any).deletions : 0 const bestScore = bestAdd + bestDel const add = typeof (item as any)?.additions === "number" ? (item as any).additions : 0 const del = typeof (item as any)?.deletions === "number" ? (item as any).deletions : 0 const score = add + del if (score > bestScore) return item if (score < bestScore) return best return String(item.file || "").localeCompare(String((best as any)?.file || "")) < 0 ? item : best }, items[0]) }) const selectedFileData = createMemo(() => { const currentSelected = props.selectedFile() const items = sorted() if (currentSelected) { const match = items.find((f) => f.file === currentSelected) if (match) return match } return mostChanged() }) const scopeKey = createMemo(() => `${props.instanceId}:${hasSession() ? sessionId() : "no-session"}`) const emptyViewerMessage = createMemo(() => { if (!hasSession()) return props.t("instanceShell.sessionChanges.noSessionSelected") const currentDiffs = diffs() if (currentDiffs === undefined) return props.t("instanceShell.sessionChanges.loading") if (!Array.isArray(currentDiffs) || currentDiffs.length === 0) return props.t("instanceShell.sessionChanges.empty") return props.t("instanceShell.filesShell.viewerEmpty") }) const headerPath = createMemo(() => { const file = selectedFileData() return file?.file ? String(file.file) : props.t("instanceShell.rightPanel.tabs.changes") }) const renderContent = (): JSX.Element => { const sortedList = sorted() const totalsValue = totals() const selected = selectedFileData() const renderViewer = () => (
0 ? selected : null} fallback={
{emptyViewerMessage()}
} > {(file) => ( {props.t("instanceInfo.loading")}
} > )}
) const renderEmptyList = () => (
{emptyViewerMessage()}
) const renderListPanel = () => ( 0} fallback={renderEmptyList()}> {(item) => (
{ props.onSelectFile(item.file, props.isPhoneLayout()) }} >
{item.file}
+{item.additions} -{item.deletions}
)}
) const renderListOverlay = () => ( 0} fallback={renderEmptyList()}> {(item) => (
{ props.onSelectFile(item.file, true) }} title={item.file} >
{item.file}
+{item.additions} -{item.deletions}
)}
) return ( {headerPath()}
+{totalsValue.additions} -{totalsValue.deletions}
} 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.changes")} /> ) } return <>{renderContent()} } export default ChangesTab