mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-07-09 16:00:52 +00:00
fix(ui): remove session diff right drawer flow (#532)
## Summary - remove the obsolete session.diff fetch/store/SSE pipeline from packages/ui - delete the right drawer Session Changes tab and Status accordion section - map stored removed "changes" tab values to Git Changes and remove unused i18n keys ## Validation - npm run typecheck --workspace @codenomad/ui - git diff --check
This commit is contained in:
parent
d9ff1e03cf
commit
b2f3e14ad1
22 changed files with 25 additions and 654 deletions
|
|
@ -136,7 +136,6 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
|
|||
activeSessions,
|
||||
activeSessionIdForInstance,
|
||||
activeSessionForInstance,
|
||||
activeSessionDiffs,
|
||||
latestTodoState,
|
||||
tokenStats,
|
||||
backgroundProcessList,
|
||||
|
|
@ -759,7 +758,6 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
|
|||
instance={props.instance}
|
||||
activeSessionId={activeSessionIdForInstance}
|
||||
activeSession={activeSessionForInstance}
|
||||
activeSessionDiffs={activeSessionDiffs}
|
||||
latestTodoState={latestTodoState}
|
||||
backgroundProcessList={backgroundProcessList}
|
||||
onOpenBackgroundOutput={openBackgroundOutput}
|
||||
|
|
@ -828,7 +826,6 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
|
|||
instance={props.instance}
|
||||
activeSessionId={activeSessionIdForInstance}
|
||||
activeSession={activeSessionForInstance}
|
||||
activeSessionDiffs={activeSessionDiffs}
|
||||
latestTodoState={latestTodoState}
|
||||
backgroundProcessList={backgroundProcessList}
|
||||
onOpenBackgroundOutput={openBackgroundOutput}
|
||||
|
|
|
|||
|
|
@ -41,10 +41,7 @@ import {
|
|||
RIGHT_PANEL_CHANGES_DIFF_CONTEXT_MODE_KEY,
|
||||
RIGHT_PANEL_CHANGES_DIFF_VIEW_MODE_KEY,
|
||||
RIGHT_PANEL_CHANGES_DIFF_WORD_WRAP_KEY,
|
||||
RIGHT_PANEL_CHANGES_LIST_OPEN_NONPHONE_KEY,
|
||||
RIGHT_PANEL_CHANGES_LIST_OPEN_PHONE_KEY,
|
||||
RIGHT_PANEL_FILES_WORD_WRAP_KEY,
|
||||
RIGHT_PANEL_CHANGES_SPLIT_WIDTH_KEY,
|
||||
RIGHT_PANEL_FILES_LIST_OPEN_NONPHONE_KEY,
|
||||
RIGHT_PANEL_FILES_LIST_OPEN_PHONE_KEY,
|
||||
RIGHT_PANEL_FILES_SPLIT_WIDTH_KEY,
|
||||
|
|
@ -62,7 +59,6 @@ import {
|
|||
readStoredRightPanelTab,
|
||||
} from "../storage"
|
||||
|
||||
const LazyChangesTab = lazy(() => import("./tabs/ChangesTab"))
|
||||
const LazyGitChangesTab = lazy(() => import("./tabs/GitChangesTab"))
|
||||
const LazyFilesTab = lazy(() => import("./tabs/FilesTab"))
|
||||
const LazyStatusTab = lazy(() => import("./tabs/StatusTab"))
|
||||
|
|
@ -79,7 +75,6 @@ interface RightPanelProps {
|
|||
|
||||
activeSessionId: Accessor<string | null>
|
||||
activeSession: Accessor<Session | null>
|
||||
activeSessionDiffs: Accessor<any[] | undefined>
|
||||
|
||||
latestTodoState: Accessor<ToolState | null>
|
||||
backgroundProcessList: Accessor<BackgroundProcess[]>
|
||||
|
|
@ -101,10 +96,9 @@ interface RightPanelProps {
|
|||
}
|
||||
|
||||
const RightPanel: Component<RightPanelProps> = (props) => {
|
||||
const [rightPanelTab, setRightPanelTab] = createSignal<RightPanelTab>(readStoredRightPanelTab("changes"))
|
||||
const defaultStatusSectionIds = ["yolo-mode", "session-changes", "plan", "background-processes", "mcp", "lsp", "plugins"]
|
||||
const [rightPanelTab, setRightPanelTab] = createSignal<RightPanelTab>(readStoredRightPanelTab("git-changes"))
|
||||
const defaultStatusSectionIds = ["yolo-mode", "plan", "background-processes", "mcp", "lsp", "plugins"]
|
||||
const [rightPanelExpandedItems, setRightPanelExpandedItems] = createSignal<string[]>(defaultStatusSectionIds)
|
||||
const [selectedFile, setSelectedFile] = createSignal<string | null>(null)
|
||||
|
||||
const [browserPath, setBrowserPath] = createSignal(".")
|
||||
const [browserEntries, setBrowserEntries] = createSignal<FileNode[] | null>(null)
|
||||
|
|
@ -131,17 +125,14 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
readStoredEnum(RIGHT_PANEL_FILES_WORD_WRAP_KEY, ["on", "off"] as const) ?? "off",
|
||||
)
|
||||
|
||||
const [changesSplitWidth, setChangesSplitWidth] = createSignal(320)
|
||||
const [filesSplitWidth, setFilesSplitWidth] = createSignal(320)
|
||||
const [gitChangesSplitWidth, setGitChangesSplitWidth] = createSignal(320)
|
||||
const [activeSplitResize, setActiveSplitResize] = createSignal<"changes" | "git-changes" | "files" | null>(null)
|
||||
const [activeSplitResize, setActiveSplitResize] = createSignal<"git-changes" | "files" | null>(null)
|
||||
const [splitResizeStartX, setSplitResizeStartX] = createSignal(0)
|
||||
const [splitResizeStartWidth, setSplitResizeStartWidth] = createSignal(0)
|
||||
|
||||
const [filesListOpen, setFilesListOpen] = createSignal(true)
|
||||
const [filesListTouched, setFilesListTouched] = createSignal(false)
|
||||
const [changesListOpen, setChangesListOpen] = createSignal(true)
|
||||
const [changesListTouched, setChangesListTouched] = createSignal(false)
|
||||
const [gitChangesListOpen, setGitChangesListOpen] = createSignal(true)
|
||||
const [gitChangesListTouched, setGitChangesListTouched] = createSignal(false)
|
||||
const [gitStagedOpen, setGitStagedOpen] = createSignal(true)
|
||||
|
|
@ -149,11 +140,8 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
|
||||
const listLayoutKey = createMemo(() => (props.isPhoneLayout() ? "phone" : "nonphone"))
|
||||
|
||||
const listOpenStorageKey = (tab: "changes" | "git-changes" | "files") => {
|
||||
const listOpenStorageKey = (tab: "git-changes" | "files") => {
|
||||
const layout = listLayoutKey()
|
||||
if (tab === "changes") {
|
||||
return layout === "phone" ? RIGHT_PANEL_CHANGES_LIST_OPEN_PHONE_KEY : RIGHT_PANEL_CHANGES_LIST_OPEN_NONPHONE_KEY
|
||||
}
|
||||
if (tab === "git-changes") {
|
||||
return layout === "phone"
|
||||
? RIGHT_PANEL_GIT_CHANGES_LIST_OPEN_PHONE_KEY
|
||||
|
|
@ -174,7 +162,7 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
: RIGHT_PANEL_GIT_CHANGES_UNSTAGED_OPEN_NONPHONE_KEY
|
||||
}
|
||||
|
||||
const persistListOpen = (tab: "changes" | "git-changes" | "files", value: boolean) => {
|
||||
const persistListOpen = (tab: "git-changes" | "files", value: boolean) => {
|
||||
if (typeof window === "undefined") return
|
||||
window.localStorage.setItem(listOpenStorageKey(tab), value ? "true" : "false")
|
||||
}
|
||||
|
|
@ -198,15 +186,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
setFilesListTouched(false)
|
||||
}
|
||||
|
||||
const changesPersisted = readStoredBool(listOpenStorageKey("changes"))
|
||||
if (changesPersisted !== null) {
|
||||
setChangesListOpen(changesPersisted)
|
||||
setChangesListTouched(true)
|
||||
} else {
|
||||
setChangesListOpen(true)
|
||||
setChangesListTouched(false)
|
||||
}
|
||||
|
||||
const gitPersisted = readStoredBool(listOpenStorageKey("git-changes"))
|
||||
if (gitPersisted !== null) {
|
||||
setGitChangesListOpen(gitPersisted)
|
||||
|
|
@ -271,19 +250,13 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
if (splitWidthsInitialized()) return
|
||||
if (!props.rightDrawerWidthInitialized()) return
|
||||
setSplitWidthsInitialized(true)
|
||||
setChangesSplitWidth(clampSplitWidth(readStoredPanelWidth(RIGHT_PANEL_CHANGES_SPLIT_WIDTH_KEY, 320)))
|
||||
setFilesSplitWidth(clampSplitWidth(readStoredPanelWidth(RIGHT_PANEL_FILES_SPLIT_WIDTH_KEY, 320)))
|
||||
setGitChangesSplitWidth(clampSplitWidth(readStoredPanelWidth(RIGHT_PANEL_GIT_CHANGES_SPLIT_WIDTH_KEY, 320)))
|
||||
})
|
||||
|
||||
const persistSplitWidth = (mode: "changes" | "git-changes" | "files", width: number) => {
|
||||
const persistSplitWidth = (mode: "git-changes" | "files", width: number) => {
|
||||
if (typeof window === "undefined") return
|
||||
const key =
|
||||
mode === "changes"
|
||||
? RIGHT_PANEL_CHANGES_SPLIT_WIDTH_KEY
|
||||
: mode === "git-changes"
|
||||
? RIGHT_PANEL_GIT_CHANGES_SPLIT_WIDTH_KEY
|
||||
: RIGHT_PANEL_FILES_SPLIT_WIDTH_KEY
|
||||
const key = mode === "git-changes" ? RIGHT_PANEL_GIT_CHANGES_SPLIT_WIDTH_KEY : RIGHT_PANEL_FILES_SPLIT_WIDTH_KEY
|
||||
window.localStorage.setItem(key, String(width))
|
||||
}
|
||||
|
||||
|
|
@ -300,16 +273,14 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
const isRtl = typeof document !== "undefined" && document.documentElement.dir === "rtl"
|
||||
const delta = (event.clientX - splitResizeStartX()) * (isRtl ? -1 : 1)
|
||||
const next = clampSplitWidth(splitResizeStartWidth() + delta)
|
||||
if (mode === "changes") setChangesSplitWidth(next)
|
||||
else if (mode === "git-changes") setGitChangesSplitWidth(next)
|
||||
if (mode === "git-changes") setGitChangesSplitWidth(next)
|
||||
else setFilesSplitWidth(next)
|
||||
}
|
||||
|
||||
function splitMouseUp() {
|
||||
const mode = activeSplitResize()
|
||||
if (mode) {
|
||||
const width =
|
||||
mode === "changes" ? changesSplitWidth() : mode === "git-changes" ? gitChangesSplitWidth() : filesSplitWidth()
|
||||
const width = mode === "git-changes" ? gitChangesSplitWidth() : filesSplitWidth()
|
||||
persistSplitWidth(mode, width)
|
||||
}
|
||||
stopSplitResize()
|
||||
|
|
@ -324,16 +295,14 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
const isRtl = typeof document !== "undefined" && document.documentElement.dir === "rtl"
|
||||
const delta = (touch.clientX - splitResizeStartX()) * (isRtl ? -1 : 1)
|
||||
const next = clampSplitWidth(splitResizeStartWidth() + delta)
|
||||
if (mode === "changes") setChangesSplitWidth(next)
|
||||
else if (mode === "git-changes") setGitChangesSplitWidth(next)
|
||||
if (mode === "git-changes") setGitChangesSplitWidth(next)
|
||||
else setFilesSplitWidth(next)
|
||||
}
|
||||
|
||||
function splitTouchEnd() {
|
||||
const mode = activeSplitResize()
|
||||
if (mode) {
|
||||
const width =
|
||||
mode === "changes" ? changesSplitWidth() : mode === "git-changes" ? gitChangesSplitWidth() : filesSplitWidth()
|
||||
const width = mode === "git-changes" ? gitChangesSplitWidth() : filesSplitWidth()
|
||||
persistSplitWidth(mode, width)
|
||||
}
|
||||
stopSplitResize()
|
||||
|
|
@ -346,22 +315,20 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
onTouchEnd: splitTouchEnd,
|
||||
})
|
||||
|
||||
const startSplitResize = (mode: "changes" | "git-changes" | "files", clientX: number) => {
|
||||
const startSplitResize = (mode: "git-changes" | "files", clientX: number) => {
|
||||
if (typeof document === "undefined") return
|
||||
setActiveSplitResize(mode)
|
||||
setSplitResizeStartX(clientX)
|
||||
setSplitResizeStartWidth(
|
||||
mode === "changes" ? changesSplitWidth() : mode === "git-changes" ? gitChangesSplitWidth() : filesSplitWidth(),
|
||||
)
|
||||
setSplitResizeStartWidth(mode === "git-changes" ? gitChangesSplitWidth() : filesSplitWidth())
|
||||
splitPointerDrag.start()
|
||||
}
|
||||
|
||||
const handleSplitResizeMouseDown = (mode: "changes" | "git-changes" | "files") => (event: MouseEvent) => {
|
||||
const handleSplitResizeMouseDown = (mode: "git-changes" | "files") => (event: MouseEvent) => {
|
||||
event.preventDefault()
|
||||
startSplitResize(mode, event.clientX)
|
||||
}
|
||||
|
||||
const handleSplitResizeTouchStart = (mode: "changes" | "git-changes" | "files") => (event: TouchEvent) => {
|
||||
const handleSplitResizeTouchStart = (mode: "git-changes" | "files") => (event: TouchEvent) => {
|
||||
const touch = event.touches[0]
|
||||
if (!touch) return
|
||||
event.preventDefault()
|
||||
|
|
@ -444,36 +411,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
setBrowserSelectedLoading(false)
|
||||
})
|
||||
|
||||
const bestDiffFile = createMemo<string | null>(() => {
|
||||
const diffs = props.activeSessionDiffs()
|
||||
if (!Array.isArray(diffs) || diffs.length === 0) return null
|
||||
const best = diffs.reduce((currentBest, item) => {
|
||||
const bestAdd = typeof (currentBest as any)?.additions === "number" ? (currentBest as any).additions : 0
|
||||
const bestDel = typeof (currentBest as any)?.deletions === "number" ? (currentBest 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 currentBest
|
||||
return String(item.file || "").localeCompare(String((currentBest as any)?.file || "")) < 0 ? item : currentBest
|
||||
}, diffs[0])
|
||||
return typeof (best as any)?.file === "string" ? (best as any).file : null
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const next = bestDiffFile()
|
||||
if (!next) return
|
||||
const diffs = props.activeSessionDiffs()
|
||||
if (!Array.isArray(diffs) || diffs.length === 0) return
|
||||
|
||||
const current = selectedFile()
|
||||
if (current && diffs.some((d) => d.file === current)) return
|
||||
setSelectedFile(next)
|
||||
})
|
||||
|
||||
const normalizeBrowserPath = (input: string) => {
|
||||
const raw = String(input || ".").trim()
|
||||
if (!raw || raw === "./") return "."
|
||||
|
|
@ -644,22 +581,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
setBrowserSelectedDirty(false)
|
||||
})
|
||||
|
||||
const handleSelectChangesFile = (file: string, closeList: boolean) => {
|
||||
setSelectedFile(file)
|
||||
if (closeList) {
|
||||
setChangesListOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
const toggleChangesList = () => {
|
||||
setChangesListTouched(true)
|
||||
setChangesListOpen((current) => {
|
||||
const next = !current
|
||||
persistListOpen("changes", next)
|
||||
return next
|
||||
})
|
||||
}
|
||||
|
||||
const toggleFilesList = () => {
|
||||
setFilesListTouched(true)
|
||||
setFilesListOpen((current) => {
|
||||
|
|
@ -730,13 +651,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
const browserScopeKey = createMemo(() => `${props.instanceId}:${worktreeSlugForViewer()}`)
|
||||
const gitScopeKey = createMemo(() => `${props.instanceId}:git:${worktreeSlugForViewer()}`)
|
||||
|
||||
const openChangesTabFromStatus = (file?: string) => {
|
||||
if (file) {
|
||||
setSelectedFile(file)
|
||||
}
|
||||
setRightPanelTab("changes")
|
||||
}
|
||||
|
||||
const handleAccordionChange = (values: string[]) => {
|
||||
setRightPanelExpandedItems(values)
|
||||
}
|
||||
|
|
@ -774,15 +688,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
<div class="tab-scroll">
|
||||
<div class="tab-strip">
|
||||
<div class="tab-strip-tabs" role="tablist" aria-label={props.t("instanceShell.rightPanel.tabs.ariaLabel")}>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
class={tabClass("changes")}
|
||||
aria-selected={rightPanelTab() === "changes"}
|
||||
onClick={() => setRightPanelTab("changes")}
|
||||
>
|
||||
<span class="tab-label">{props.t("instanceShell.rightPanel.tabs.changes")}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
|
|
@ -819,31 +724,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
</div>
|
||||
|
||||
<div class="flex-1 overflow-y-auto">
|
||||
<Show when={rightPanelTab() === "changes"}>
|
||||
<Suspense fallback={<RightPanelTabFallback />}>
|
||||
<LazyChangesTab
|
||||
t={props.t}
|
||||
instanceId={props.instanceId}
|
||||
activeSessionId={props.activeSessionId}
|
||||
activeSessionDiffs={props.activeSessionDiffs}
|
||||
selectedFile={selectedFile}
|
||||
onSelectFile={handleSelectChangesFile}
|
||||
diffViewMode={diffViewMode}
|
||||
diffContextMode={diffContextMode}
|
||||
diffWordWrapMode={diffWordWrapMode}
|
||||
onViewModeChange={setDiffViewMode}
|
||||
onContextModeChange={setDiffContextMode}
|
||||
onWordWrapModeChange={setDiffWordWrapMode}
|
||||
listOpen={changesListOpen}
|
||||
onToggleList={toggleChangesList}
|
||||
splitWidth={changesSplitWidth}
|
||||
onResizeMouseDown={handleSplitResizeMouseDown("changes")}
|
||||
onResizeTouchStart={handleSplitResizeTouchStart("changes")}
|
||||
isPhoneLayout={props.isPhoneLayout}
|
||||
/>
|
||||
</Suspense>
|
||||
</Show>
|
||||
|
||||
<Show when={rightPanelTab() === "git-changes"}>
|
||||
<Suspense fallback={<RightPanelTabFallback />}>
|
||||
<LazyGitChangesTab
|
||||
|
|
@ -939,7 +819,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
instance={props.instance}
|
||||
activeSessionId={props.activeSessionId}
|
||||
activeSession={props.activeSession}
|
||||
activeSessionDiffs={props.activeSessionDiffs}
|
||||
latestTodoState={props.latestTodoState}
|
||||
backgroundProcessList={props.backgroundProcessList}
|
||||
onOpenBackgroundOutput={props.onOpenBackgroundOutput}
|
||||
|
|
@ -947,7 +826,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
|
|||
onTerminateBackgroundProcess={props.onTerminateBackgroundProcess}
|
||||
expandedItems={rightPanelExpandedItems}
|
||||
onExpandedItemsChange={handleAccordionChange}
|
||||
onOpenChangesTab={openChangesTabFromStatus}
|
||||
/>
|
||||
</Suspense>
|
||||
</Show>
|
||||
|
|
|
|||
|
|
@ -1,240 +0,0 @@
|
|||
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, any>) => string
|
||||
|
||||
instanceId: string
|
||||
activeSessionId: Accessor<string | null>
|
||||
activeSessionDiffs: Accessor<any[] | undefined>
|
||||
|
||||
selectedFile: Accessor<string | null>
|
||||
onSelectFile: (file: string, closeList: boolean) => void
|
||||
|
||||
diffViewMode: Accessor<DiffViewMode>
|
||||
diffContextMode: Accessor<DiffContextMode>
|
||||
diffWordWrapMode: Accessor<DiffWordWrapMode>
|
||||
onViewModeChange: (mode: DiffViewMode) => void
|
||||
onContextModeChange: (mode: DiffContextMode) => void
|
||||
onWordWrapModeChange: (mode: DiffWordWrapMode) => void
|
||||
|
||||
listOpen: Accessor<boolean>
|
||||
onToggleList: () => void
|
||||
splitWidth: Accessor<number>
|
||||
onResizeMouseDown: (event: MouseEvent) => void
|
||||
onResizeTouchStart: (event: TouchEvent) => void
|
||||
isPhoneLayout: Accessor<boolean>
|
||||
}
|
||||
|
||||
const ChangesTab: Component<ChangesTabProps> = (props) => {
|
||||
const sessionId = createMemo(() => props.activeSessionId())
|
||||
const hasSession = createMemo(() => Boolean(sessionId() && sessionId() !== "info"))
|
||||
const diffs = createMemo(() => (hasSession() ? props.activeSessionDiffs() : null))
|
||||
|
||||
const sorted = createMemo<any[]>(() => {
|
||||
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<any | null>(() => {
|
||||
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<any | null>(() => {
|
||||
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 = () => (
|
||||
<div class="file-viewer-panel flex-1">
|
||||
<div class="file-viewer-content file-viewer-content--monaco">
|
||||
<Show
|
||||
when={selected && hasSession() && sortedList.length > 0 ? selected : null}
|
||||
fallback={
|
||||
<div class="file-viewer-empty">
|
||||
<span class="file-viewer-empty-text">{emptyViewerMessage()}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{(file) => (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div class="file-viewer-empty">
|
||||
<span class="file-viewer-empty-text">{props.t("instanceInfo.loading")}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<LazyMonacoDiffViewer
|
||||
scopeKey={scopeKey()}
|
||||
path={String(file().file || "")}
|
||||
patch={String((file() as any).patch || "")}
|
||||
viewMode={props.diffViewMode()}
|
||||
contextMode={props.diffContextMode()}
|
||||
wordWrap={props.diffWordWrapMode()}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const renderEmptyList = () => (
|
||||
<div class="p-3 text-xs text-secondary">{emptyViewerMessage()}</div>
|
||||
)
|
||||
|
||||
const renderListPanel = () => (
|
||||
<Show when={sortedList.length > 0} fallback={renderEmptyList()}>
|
||||
<For each={sortedList}>
|
||||
{(item) => (
|
||||
<div
|
||||
class={`file-list-item ${selected?.file === item.file ? "file-list-item-active" : ""}`}
|
||||
onClick={() => {
|
||||
props.onSelectFile(item.file, props.isPhoneLayout())
|
||||
}}
|
||||
>
|
||||
<div class="file-list-item-content">
|
||||
<div class="file-list-item-path" title={item.file}>
|
||||
<span class="file-path-text">{item.file}</span>
|
||||
</div>
|
||||
<div class="file-list-item-stats">
|
||||
<span class="file-list-item-additions">+{item.additions}</span>
|
||||
<span class="file-list-item-deletions">-{item.deletions}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
)
|
||||
|
||||
const renderListOverlay = () => (
|
||||
<Show when={sortedList.length > 0} fallback={renderEmptyList()}>
|
||||
<For each={sortedList}>
|
||||
{(item) => (
|
||||
<div
|
||||
class={`file-list-item ${selected?.file === item.file ? "file-list-item-active" : ""}`}
|
||||
onClick={() => {
|
||||
props.onSelectFile(item.file, true)
|
||||
}}
|
||||
title={item.file}
|
||||
>
|
||||
<div class="file-list-item-content">
|
||||
<div class="file-list-item-path" title={item.file}>
|
||||
<span class="file-path-text">{item.file}</span>
|
||||
</div>
|
||||
<div class="file-list-item-stats">
|
||||
<span class="file-list-item-additions">+{item.additions}</span>
|
||||
<span class="file-list-item-deletions">-{item.deletions}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
)
|
||||
|
||||
return (
|
||||
<SplitFilePanel
|
||||
header={
|
||||
<>
|
||||
<span class="files-tab-selected-path" title={headerPath()}>
|
||||
<span class="file-path-text">{headerPath()}</span>
|
||||
</span>
|
||||
|
||||
<div class="files-tab-stats" style={{ flex: "0 0 auto" }}>
|
||||
<span class="files-tab-stat files-tab-stat-additions">
|
||||
<span class="files-tab-stat-value">+{totalsValue.additions}</span>
|
||||
</span>
|
||||
<span class="files-tab-stat files-tab-stat-deletions">
|
||||
<span class="files-tab-stat-value">-{totalsValue.deletions}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ "margin-left": "auto" }}>
|
||||
<DiffToolbar
|
||||
viewMode={props.diffViewMode()}
|
||||
contextMode={props.diffContextMode()}
|
||||
wordWrapMode={props.diffWordWrapMode()}
|
||||
onViewModeChange={props.onViewModeChange}
|
||||
onContextModeChange={props.onContextModeChange}
|
||||
onWordWrapModeChange={props.onWordWrapModeChange}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
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
|
||||
|
|
@ -24,7 +24,6 @@ interface StatusTabProps {
|
|||
|
||||
activeSessionId: Accessor<string | null>
|
||||
activeSession: Accessor<Session | null>
|
||||
activeSessionDiffs: Accessor<any[] | undefined>
|
||||
|
||||
latestTodoState: Accessor<ToolState | null>
|
||||
|
||||
|
|
@ -36,7 +35,6 @@ interface StatusTabProps {
|
|||
expandedItems: Accessor<string[]>
|
||||
onExpandedItemsChange: (values: string[]) => void
|
||||
|
||||
onOpenChangesTab: (file?: string) => void
|
||||
}
|
||||
|
||||
const StatusTab: Component<StatusTabProps> = (props) => {
|
||||
|
|
@ -71,85 +69,6 @@ const StatusTab: Component<StatusTabProps> = (props) => {
|
|||
)
|
||||
}
|
||||
|
||||
const renderStatusSessionChanges = () => {
|
||||
const sessionId = props.activeSessionId()
|
||||
if (!sessionId || sessionId === "info") {
|
||||
return (
|
||||
<div class="right-panel-empty right-panel-empty--left">
|
||||
<span class="text-xs">{props.t("instanceShell.sessionChanges.noSessionSelected")}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const diffs = props.activeSessionDiffs()
|
||||
if (diffs === undefined) {
|
||||
return (
|
||||
<div class="right-panel-empty right-panel-empty--left">
|
||||
<span class="text-xs">{props.t("instanceShell.sessionChanges.loading")}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!Array.isArray(diffs) || diffs.length === 0) {
|
||||
return (
|
||||
<div class="right-panel-empty right-panel-empty--left">
|
||||
<span class="text-xs">{props.t("instanceShell.sessionChanges.empty")}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const sorted = [...diffs].sort((a, b) => String(a.file || "").localeCompare(String(b.file || "")))
|
||||
const totals = 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 },
|
||||
)
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-3 min-h-0">
|
||||
<div class="flex items-center justify-between gap-2 text-[11px] text-secondary">
|
||||
<span>{props.t("instanceShell.sessionChanges.filesChanged", { count: sorted.length })}</span>
|
||||
<span class="flex items-center gap-2">
|
||||
<span style={{ color: "var(--session-status-idle-fg)" }}>{`+${totals.additions}`}</span>
|
||||
<span style={{ color: "var(--session-status-working-fg)" }}>{`-${totals.deletions}`}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="rounded-md border border-base bg-surface-secondary p-2 max-h-[40vh] overflow-y-auto">
|
||||
<div class="flex flex-col">
|
||||
<For each={sorted}>
|
||||
{(item) => (
|
||||
<button
|
||||
type="button"
|
||||
class="border-b border-base last:border-b-0 text-left hover:bg-surface-muted rounded-sm"
|
||||
onClick={() => props.onOpenChangesTab(item.file)}
|
||||
title={props.t("instanceShell.sessionChanges.actions.show")}
|
||||
>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div
|
||||
class="text-xs font-mono text-primary min-w-0 flex-1 overflow-hidden whitespace-nowrap"
|
||||
title={item.file}
|
||||
style="text-overflow: ellipsis; direction: rtl; text-align: left; unicode-bidi: plaintext;"
|
||||
>
|
||||
{item.file}
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-[11px] flex-shrink-0">
|
||||
<span style={{ color: "var(--session-status-idle-fg)" }}>{`+${item.additions}`}</span>
|
||||
<span style={{ color: "var(--session-status-working-fg)" }}>{`-${item.deletions}`}</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const renderPlanSectionContent = () => {
|
||||
const sessionId = props.activeSessionId()
|
||||
if (!sessionId || sessionId === "info") {
|
||||
|
|
@ -260,12 +179,6 @@ const StatusTab: Component<StatusTabProps> = (props) => {
|
|||
tooltipKey: "instanceShell.rightPanel.sections.yoloMode.tooltip",
|
||||
render: renderYoloModeSection,
|
||||
},
|
||||
{
|
||||
id: "session-changes",
|
||||
labelKey: "instanceShell.rightPanel.sections.sessionChanges",
|
||||
tooltipKey: "instanceShell.rightPanel.sections.sessionChanges.tooltip",
|
||||
render: renderStatusSessionChanges,
|
||||
},
|
||||
{
|
||||
id: "plan",
|
||||
labelKey: "instanceShell.rightPanel.sections.plan",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export type RightPanelTab = "changes" | "git-changes" | "files" | "status"
|
||||
export type RightPanelTab = "git-changes" | "files" | "status"
|
||||
|
||||
export type DiffViewMode = "split" | "unified"
|
||||
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ export function useGitChanges(options: UseGitChangesOptions) {
|
|||
if (event.type !== "instance.event") return
|
||||
if (event.instanceId !== options.instanceId) return
|
||||
const eventType = (event.event as { type?: unknown } | undefined)?.type
|
||||
if (eventType !== "session.updated" && eventType !== "session.diff") return
|
||||
if (eventType !== "session.updated") return
|
||||
void passiveRefreshGitStatus({ forceReloadSelectedDiff: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,8 @@ export const LEFT_PIN_STORAGE_KEY = "opencode-session-left-drawer-pinned-v1"
|
|||
export const RIGHT_PIN_STORAGE_KEY = "opencode-session-right-drawer-pinned-v1"
|
||||
export const RIGHT_PANEL_TAB_STORAGE_KEY = "opencode-session-right-panel-tab-v2"
|
||||
export const LEGACY_RIGHT_PANEL_TAB_STORAGE_KEY = "opencode-session-right-panel-tab-v1"
|
||||
export const RIGHT_PANEL_CHANGES_SPLIT_WIDTH_KEY = "opencode-session-right-panel-changes-split-width-v1"
|
||||
export const RIGHT_PANEL_FILES_SPLIT_WIDTH_KEY = "opencode-session-right-panel-files-split-width-v1"
|
||||
export const RIGHT_PANEL_GIT_CHANGES_SPLIT_WIDTH_KEY = "opencode-session-right-panel-git-changes-split-width-v1"
|
||||
export const RIGHT_PANEL_CHANGES_LIST_OPEN_NONPHONE_KEY = "opencode-session-right-panel-changes-list-open-nonphone-v1"
|
||||
export const RIGHT_PANEL_CHANGES_LIST_OPEN_PHONE_KEY = "opencode-session-right-panel-changes-list-open-phone-v1"
|
||||
export const RIGHT_PANEL_FILES_LIST_OPEN_NONPHONE_KEY = "opencode-session-right-panel-files-list-open-nonphone-v1"
|
||||
export const RIGHT_PANEL_FILES_LIST_OPEN_PHONE_KEY = "opencode-session-right-panel-files-list-open-phone-v1"
|
||||
export const RIGHT_PANEL_GIT_CHANGES_LIST_OPEN_NONPHONE_KEY = "opencode-session-right-panel-git-changes-list-open-nonphone-v1"
|
||||
|
|
@ -55,13 +52,13 @@ export function persistPinState(side: "left" | "right", value: boolean) {
|
|||
}
|
||||
|
||||
export function readStoredRightPanelTab(
|
||||
defaultValue: "changes" | "git-changes" | "files" | "status",
|
||||
): "changes" | "git-changes" | "files" | "status" {
|
||||
defaultValue: "git-changes" | "files" | "status",
|
||||
): "git-changes" | "files" | "status" {
|
||||
if (typeof window === "undefined") return defaultValue
|
||||
|
||||
const stored = window.localStorage.getItem(RIGHT_PANEL_TAB_STORAGE_KEY)
|
||||
if (stored === "status") return "status"
|
||||
if (stored === "changes") return "changes"
|
||||
if (stored === "changes") return "git-changes"
|
||||
if (stored === "git-changes") return "git-changes"
|
||||
if (stored === "files") return "files"
|
||||
|
||||
|
|
@ -69,7 +66,7 @@ export function readStoredRightPanelTab(
|
|||
const legacy = window.localStorage.getItem(LEGACY_RIGHT_PANEL_TAB_STORAGE_KEY)
|
||||
if (legacy === "status") return "status"
|
||||
if (legacy === "browser") return "files"
|
||||
if (legacy === "files") return "changes"
|
||||
if (legacy === "files") return "git-changes"
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ type InstanceSessionContextState = {
|
|||
activeSessionIdForInstance: Accessor<string | null>
|
||||
parentSessionIdForInstance: Accessor<string | null>
|
||||
activeSessionForInstance: Accessor<SessionFamilyMember | null>
|
||||
activeSessionDiffs: Accessor<SessionFamilyMember["diff"] | undefined>
|
||||
|
||||
// Usage / info summaries
|
||||
activeSessionUsage: Accessor<SessionUsageState | null>
|
||||
|
|
@ -77,11 +76,6 @@ export function useInstanceSessionContext(options: InstanceSessionContextOptions
|
|||
return activeSessions().get(sessionId) ?? null
|
||||
})
|
||||
|
||||
const activeSessionDiffs = createMemo(() => {
|
||||
const session = activeSessionForInstance()
|
||||
return session?.diff
|
||||
})
|
||||
|
||||
const activeSessionUsage = createMemo(() => {
|
||||
const sessionId = activeSessionIdForInstance()
|
||||
if (!sessionId) return null
|
||||
|
|
@ -161,7 +155,6 @@ export function useInstanceSessionContext(options: InstanceSessionContextOptions
|
|||
activeSessionIdForInstance,
|
||||
parentSessionIdForInstance,
|
||||
activeSessionForInstance,
|
||||
activeSessionDiffs,
|
||||
activeSessionUsage,
|
||||
activeSessionInfoDetails,
|
||||
tokenStats,
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "Senden Sie eine Nachricht, um eine neue Sitzung zu erstellen, oder wählen Sie eine bestehende Sitzung aus.",
|
||||
|
||||
"instanceShell.rightPanel.title": "Status-Panel",
|
||||
"instanceShell.rightPanel.tabs.changes": "Sitzungsänderungen",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Git-Änderungen",
|
||||
"instanceShell.rightPanel.tabs.files": "Dateien",
|
||||
"instanceShell.rightPanel.tabs.status": "Status",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "Datei konnte nicht gespeichert werden",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Yolo-Modus",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "Genehmigt Berechtigungsanfragen für die aktuelle Sitzung automatisch. Nur verwenden, wenn Sie den ausgeführten Tools vertrauen.",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "Sitzungsänderungen",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "In der aktuellen Sitzung geänderte Dateien. Zeigt Hinzufügungen und Löschungen für jede Datei.",
|
||||
"instanceShell.rightPanel.sections.plan": "Plan",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "Die Roadmap des Agenten für diese Sitzung. Verfolgt Aufgaben, Unteraufgaben und deren Abschlussstatus.",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "Hintergrund-Shells",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "Plugins",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "Plugins, die die Benutzeroberfläche und das Serververhalten anpassen.",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "Wählen Sie eine Sitzung aus, um Änderungen zu sehen.",
|
||||
"instanceShell.sessionChanges.loading": "Sitzungsänderungen werden abgerufen...",
|
||||
"instanceShell.sessionChanges.empty": "Noch keine Sitzungsänderungen.",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} Dateien geändert",
|
||||
"instanceShell.sessionChanges.actions.show": "Änderungen anzeigen",
|
||||
|
||||
"instanceShell.gitChanges.noSessionSelected": "Wählen Sie eine Sitzung aus, um Git-Änderungen zu sehen.",
|
||||
"instanceShell.gitChanges.loading": "Git-Änderungen werden geladen...",
|
||||
"instanceShell.gitChanges.empty": "Noch keine Git-Änderungen.",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "Send a message below to create a new session, or select an existing session to continue.",
|
||||
|
||||
"instanceShell.rightPanel.title": "Status Panel",
|
||||
"instanceShell.rightPanel.tabs.changes": "Session Changes",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Git Changes",
|
||||
"instanceShell.rightPanel.tabs.files": "Files",
|
||||
"instanceShell.rightPanel.tabs.status": "Status",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "Failed to save file",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Yolo Mode",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "Automatically approves permission requests for the current session. Use it only when you trust the tools being run.",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "Session Changes",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "Files modified in the current session. Shows additions and deletions for each file.",
|
||||
"instanceShell.rightPanel.sections.plan": "Plan",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "The agent's roadmap for this session. Tracks tasks, subtasks, and their completion status.",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "Background Shells",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "Plugins",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "Plugins that customize the UI and server behavior, adding features beyond MCP and LSP.",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "Select a session to view changes.",
|
||||
"instanceShell.sessionChanges.loading": "Fetching session changes...",
|
||||
"instanceShell.sessionChanges.empty": "No session changes yet.",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} files changed",
|
||||
"instanceShell.sessionChanges.actions.show": "Show changes",
|
||||
|
||||
"instanceShell.gitChanges.noSessionSelected": "Select a session to view git changes.",
|
||||
"instanceShell.gitChanges.loading": "Loading git changes...",
|
||||
"instanceShell.gitChanges.empty": "No git changes yet.",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "Envía un mensaje abajo para crear una nueva sesión, o selecciona una sesión existente para continuar.",
|
||||
|
||||
"instanceShell.rightPanel.title": "Panel de estado",
|
||||
"instanceShell.rightPanel.tabs.changes": "Cambios",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Cambios de Git",
|
||||
"instanceShell.rightPanel.tabs.files": "Archivos",
|
||||
"instanceShell.rightPanel.tabs.status": "Estado",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "Error al guardar el archivo",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Modo yolo",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "Aprueba automaticamente las solicitudes de permiso de la sesion actual. Usalo solo si confias en las herramientas que se estan ejecutando.",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "Cambios de sesión",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "Archivos modificados en la sesión actual. Muestra las adiciones y eliminaciones de cada archivo.",
|
||||
"instanceShell.rightPanel.sections.plan": "Plan",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "Hoja de ruta del agente para esta sesión. Realiza el seguimiento de tareas, subtareas y su estado de finalización.",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "Shells en segundo plano",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "Plugins",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "Plugins que personalizan el comportamiento de la UI y del servidor, y añaden funciones más allá de MCP y LSP.",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "Selecciona una sesión para ver los cambios.",
|
||||
"instanceShell.sessionChanges.loading": "Obteniendo cambios de la sesión...",
|
||||
"instanceShell.sessionChanges.empty": "Aún no hay cambios.",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} archivos cambiados",
|
||||
"instanceShell.sessionChanges.actions.show": "Mostrar cambios",
|
||||
|
||||
"instanceShell.gitChanges.loading": "Cargando cambios de Git...",
|
||||
"instanceShell.gitChanges.empty": "Aún no hay cambios de Git.",
|
||||
"instanceShell.gitChanges.deleted": "Eliminado",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "Envoyez un message ci-dessous pour créer une nouvelle session, ou sélectionnez une session existante pour continuer.",
|
||||
|
||||
"instanceShell.rightPanel.title": "Panneau d'état",
|
||||
"instanceShell.rightPanel.tabs.changes": "Modifications",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Changements Git",
|
||||
"instanceShell.rightPanel.tabs.files": "Fichiers",
|
||||
"instanceShell.rightPanel.tabs.status": "Statut",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "Échec de l'enregistrement du fichier",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Mode yolo",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "Approuve automatiquement les demandes d'autorisation pour la session actuelle. A utiliser seulement si vous faites confiance aux outils executes.",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "Changements de session",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "Fichiers modifiés dans la session actuelle. Affiche les ajouts et suppressions pour chaque fichier.",
|
||||
"instanceShell.rightPanel.sections.plan": "Plan",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "Feuille de route de l'agent pour cette session. Suit les tâches et leur statut d'achèvement.",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "Shells en arrière-plan",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "Plugins",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "Plugins qui personnalisent le comportement de l'UI et du serveur, ajoutant des fonctionnalités au-delà de MCP et LSP.",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "Sélectionnez une session pour voir les changements.",
|
||||
"instanceShell.sessionChanges.loading": "Récupération des changements...",
|
||||
"instanceShell.sessionChanges.empty": "Aucun changement pour l'instant.",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} fichiers modifiés",
|
||||
"instanceShell.sessionChanges.actions.show": "Afficher les changements",
|
||||
|
||||
"instanceShell.gitChanges.loading": "Chargement des changements Git...",
|
||||
"instanceShell.gitChanges.empty": "Aucun changement Git pour l'instant.",
|
||||
"instanceShell.gitChanges.deleted": "Supprimé",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "שלח הודעה למטה כדי ליצור סשן חדש, או בחר סשן קיים כדי להמשיך.",
|
||||
|
||||
"instanceShell.rightPanel.title": "לוח סטטוס",
|
||||
"instanceShell.rightPanel.tabs.changes": "שינויי סשן",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "שינויי Git",
|
||||
"instanceShell.rightPanel.tabs.files": "קבצים",
|
||||
"instanceShell.rightPanel.tabs.status": "סטטוס",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "כשלון בשמירת הקובץ",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "מצב Yolo",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "מאשר אוטומטית בקשות הרשאה עבור הסשן הנוכחי. השתמשו בזה רק אם אתם סומכים על הכלים שרצים.",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "שינויי סשן",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "קבצים שהשתנו בסשן הנוכחי. מציג הוספות ומחיקות לכל קובץ.",
|
||||
"instanceShell.rightPanel.sections.plan": "תוכנית",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "מפת הדרכים של הסוכן לסשן זה. עוקב אחר משימות, תת-משימות וסטטוס השלמתן.",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "מעטפות רקע",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "תוספים",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "תוספים המתאימים אישית את הממשק ואת התנהגות השרת, ומוסיפים תכונות מעבר ל-MCP ו-LSP.",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "בחר סשן לצפייה בשינויים.",
|
||||
"instanceShell.sessionChanges.loading": "מאחזר שינויי סשן...",
|
||||
"instanceShell.sessionChanges.empty": "אין שינויי סשן עדיין.",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} קבצים שונו",
|
||||
"instanceShell.sessionChanges.actions.show": "הצג שינויים",
|
||||
|
||||
"instanceShell.filesShell.fileListTitle": "רשימת קבצים",
|
||||
"instanceShell.filesShell.mobileSelectorLabel": "בחר קובץ",
|
||||
"instanceShell.filesShell.mobileSelectorEmpty": "בחר קובץ",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "下にメッセージを送信して新しいセッションを作成するか、既存のセッションを選択して続行してください。",
|
||||
|
||||
"instanceShell.rightPanel.title": "ステータスパネル",
|
||||
"instanceShell.rightPanel.tabs.changes": "変更",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Git 変更",
|
||||
"instanceShell.rightPanel.tabs.files": "ファイル",
|
||||
"instanceShell.rightPanel.tabs.status": "ステータス",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "ファイルの保存に失敗しました",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Yoloモード",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "現在のセッションの権限リクエストを自動承認します。実行中のツールを信頼できる場合にのみ使用してください。",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "セッション変更",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "現在のセッションで変更されたファイル。各ファイルの追加と削除を表示します。",
|
||||
"instanceShell.rightPanel.sections.plan": "計画",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "このセッションにおけるエージェントのロードマップ。タスクやサブタスク、および完了状況を追跡します。",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "バックグラウンドシェル",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "プラグイン",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "UI とサーバーの動作をカスタマイズし、MCP や LSP 以外の機能も追加できるプラグイン。",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "変更を表示するにはセッションを選択してください。",
|
||||
"instanceShell.sessionChanges.loading": "変更を取得中...",
|
||||
"instanceShell.sessionChanges.empty": "まだ変更はありません。",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} 個のファイルが変更されました",
|
||||
"instanceShell.sessionChanges.actions.show": "変更を表示",
|
||||
|
||||
"instanceShell.gitChanges.loading": "Git の変更を読み込み中...",
|
||||
"instanceShell.gitChanges.empty": "Git の変更はまだありません。",
|
||||
"instanceShell.gitChanges.deleted": "削除済み",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "नयाँ सत्र सिर्जना गर्न तल सन्देश पठाउनुहोस्, वा जारी राख्न अवस्थित सत्र चयन गर्नुहोस्।",
|
||||
|
||||
"instanceShell.rightPanel.title": "स्थिति प्यानल (Status Panel)",
|
||||
"instanceShell.rightPanel.tabs.changes": "सत्र परिवर्तनहरू",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Git परिवर्तनहरू",
|
||||
"instanceShell.rightPanel.tabs.files": "फाइलहरू",
|
||||
"instanceShell.rightPanel.tabs.status": "स्थिति",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "फाइल बचत गर्न असफल भयो",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Yolo मोड",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "हालको सत्रको लागि अनुमति अनुरोधहरू स्वतः स्वीकृत गर्दछ। तपाईंले चलिरहेका उपकरणहरूलाई विश्वास गर्दा मात्र यसलाई प्रयोग गर्नुहोस्।",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "सत्र परिवर्तनहरू",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "हालको सत्रमा परिमार्जन गरिएका फाइलहरू।",
|
||||
"instanceShell.rightPanel.sections.plan": "योजना",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "यस सत्रको लागि एजेन्टको मार्गचित्र।",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "पृष्ठभूमि शेलहरू",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "प्लगइनहरू",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "UI र सर्भर व्यवहार अनुकूलित गर्ने प्लगइनहरू।",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "परिवर्तनहरू हेर्न सत्र चयन गर्नुहोस्।",
|
||||
"instanceShell.sessionChanges.loading": "सत्र परिवर्तनहरू प्राप्त गर्दै...",
|
||||
"instanceShell.sessionChanges.empty": "अझै कुनै सत्र परिवर्तनहरू छैनन्।",
|
||||
"instanceShell.sessionChanges.filesChanged": "{count} फाइलहरू परिवर्तन भए",
|
||||
"instanceShell.sessionChanges.actions.show": "परिवर्तनहरू देखाउनुहोस्",
|
||||
|
||||
"instanceShell.gitChanges.noSessionSelected": "Git परिवर्तनहरू हेर्न सत्र चयन गर्नुहोस्।",
|
||||
"instanceShell.gitChanges.loading": "Git परिवर्तनहरू लोड गर्दै...",
|
||||
"instanceShell.gitChanges.empty": "अझै कुनै Git परिवर्तनहरू छैनन्।",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "Отправьте сообщение ниже, чтобы создать новую сессию, или выберите существующую сессию, чтобы продолжить.",
|
||||
|
||||
"instanceShell.rightPanel.title": "Панель состояния",
|
||||
"instanceShell.rightPanel.tabs.changes": "Изменения",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Изменения Git",
|
||||
"instanceShell.rightPanel.tabs.files": "Файлы",
|
||||
"instanceShell.rightPanel.tabs.status": "Статус",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "Не удалось сохранить файл",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Режим Yolo",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "Автоматически одобряет запросы разрешений для текущей сессии. Включайте только если доверяете запускаемым инструментам.",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "Изменения сессии",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "Файлы, измененные в текущей сессии. Показывает добавления и удаления для каждого файла.",
|
||||
"instanceShell.rightPanel.sections.plan": "План",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "Дорожная карта агента для этой сессии. Отслеживает задачи и их статус выполнения.",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "Фоновые оболочки",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "Плагины",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "Плагины, настраивающие поведение интерфейса и сервера, добавляющие функции поверх MCP и LSP.",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "Выберите сессию, чтобы просмотреть изменения.",
|
||||
"instanceShell.sessionChanges.loading": "Загрузка изменений...",
|
||||
"instanceShell.sessionChanges.empty": "Пока нет изменений.",
|
||||
"instanceShell.sessionChanges.filesChanged": "Изменено файлов: {count}",
|
||||
"instanceShell.sessionChanges.actions.show": "Показать изменения",
|
||||
|
||||
"instanceShell.gitChanges.loading": "Загрузка изменений Git...",
|
||||
"instanceShell.gitChanges.empty": "Изменений Git пока нет.",
|
||||
"instanceShell.gitChanges.deleted": "Удалено",
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ export const instanceMessages = {
|
|||
"instanceShell.empty.description": "在下方发送消息以创建新会话,或选择现有会话继续。",
|
||||
|
||||
"instanceShell.rightPanel.title": "状态面板",
|
||||
"instanceShell.rightPanel.tabs.changes": "更改",
|
||||
"instanceShell.rightPanel.tabs.gitChanges": "Git 更改",
|
||||
"instanceShell.rightPanel.tabs.files": "文件",
|
||||
"instanceShell.rightPanel.tabs.status": "状态",
|
||||
|
|
@ -109,8 +108,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.toast.saveError": "保存文件失败",
|
||||
"instanceShell.rightPanel.sections.yoloMode": "Yolo 模式",
|
||||
"instanceShell.rightPanel.sections.yoloMode.tooltip": "自动批准当前会话的权限请求。仅在你信任正在运行的工具时启用。",
|
||||
"instanceShell.rightPanel.sections.sessionChanges": "会话更改",
|
||||
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "当前会话中修改的文件。显示每个文件的添加和删除。",
|
||||
"instanceShell.rightPanel.sections.plan": "计划",
|
||||
"instanceShell.rightPanel.sections.plan.tooltip": "代理的路线图。跟踪任务、子任务及其完成状态。",
|
||||
"instanceShell.rightPanel.sections.backgroundProcesses": "后台 Shell",
|
||||
|
|
@ -122,12 +119,6 @@ export const instanceMessages = {
|
|||
"instanceShell.rightPanel.sections.plugins": "插件",
|
||||
"instanceShell.rightPanel.sections.plugins.tooltip": "自定义 UI 和服务器行为的插件,添加超出 MCP 和 LSP 的功能。",
|
||||
|
||||
"instanceShell.sessionChanges.noSessionSelected": "选择会话以查看更改。",
|
||||
"instanceShell.sessionChanges.loading": "正在获取会话更改...",
|
||||
"instanceShell.sessionChanges.empty": "暂无会话更改。",
|
||||
"instanceShell.sessionChanges.filesChanged": "已更改 {count} 个文件",
|
||||
"instanceShell.sessionChanges.actions.show": "显示更改",
|
||||
|
||||
"instanceShell.gitChanges.loading": "正在加载 Git 更改...",
|
||||
"instanceShell.gitChanges.empty": "暂无 Git 更改。",
|
||||
"instanceShell.gitChanges.deleted": "已删除",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import type {
|
|||
EventLspUpdated,
|
||||
|
||||
EventSessionCompacted,
|
||||
EventSessionDiff,
|
||||
EventSessionError,
|
||||
EventSessionIdle,
|
||||
EventSessionUpdated,
|
||||
|
|
@ -77,7 +76,6 @@ type SSEEvent =
|
|||
| MessagePartDeltaEvent
|
||||
| EventSessionUpdated
|
||||
| EventSessionCompacted
|
||||
| EventSessionDiff
|
||||
| EventSessionError
|
||||
| EventSessionIdle
|
||||
| EventSessionStatus
|
||||
|
|
@ -168,9 +166,6 @@ class SSEManager {
|
|||
case "session.status":
|
||||
this.onSessionStatus?.(instanceId, event as EventSessionStatus)
|
||||
break
|
||||
case "session.diff":
|
||||
this.onSessionDiff?.(instanceId, event as EventSessionDiff)
|
||||
break
|
||||
case "permission.asked":
|
||||
case "permission.updated":
|
||||
this.onPermissionUpdated?.(instanceId, event as any)
|
||||
|
|
@ -234,7 +229,6 @@ class SSEManager {
|
|||
onTuiToast?: (instanceId: string, event: TuiToastEvent) => void
|
||||
onSessionIdle?: (instanceId: string, event: EventSessionIdle) => void
|
||||
onSessionStatus?: (instanceId: string, event: EventSessionStatus) => void
|
||||
onSessionDiff?: (instanceId: string, event: EventSessionDiff) => void
|
||||
onPermissionUpdated?: (instanceId: string, event: EventPermissionV2Asked | LegacyPermissionAskedEvent) => void
|
||||
onPermissionReplied?: (instanceId: string, event: EventPermissionV2Replied | LegacyPermissionRepliedEvent) => void
|
||||
onQuestionAsked?: (instanceId: string, event: EventQuestionV2Asked | { type: "question.asked"; properties?: any }) => void
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
type SessionStatus,
|
||||
} from "../types/session"
|
||||
import type { Message } from "../types/message"
|
||||
import type { SnapshotFileDiff, SessionV2Info, V2SessionsResponse } from "@opencode-ai/sdk/v2/client"
|
||||
import type { SessionV2Info, V2SessionsResponse } from "@opencode-ai/sdk/v2/client"
|
||||
|
||||
import { instances } from "./instances"
|
||||
import { preferences, setAgentModelPreference } from "./preferences"
|
||||
|
|
@ -61,8 +61,6 @@ import { hydrateSessionMetadataWithClient } from "./session-metadata"
|
|||
|
||||
const log = getLogger("api")
|
||||
|
||||
const pendingSessionDiffFetches = new Map<string, Promise<void>>()
|
||||
|
||||
function getErrorMessage(error: unknown): string {
|
||||
if (!error) return "Failed to load messages"
|
||||
|
||||
|
|
@ -89,46 +87,6 @@ async function getSessionWorkspacePayload(instanceId: string, sessionId: string)
|
|||
return workspace ? { workspace } : {}
|
||||
}
|
||||
|
||||
async function loadSessionDiff(instanceId: string, sessionId: string, force = false): Promise<void> {
|
||||
if (!instanceId || !sessionId) return
|
||||
|
||||
const key = `${instanceId}:${sessionId}`
|
||||
if (!force) {
|
||||
const existing = sessions().get(instanceId)?.get(sessionId)
|
||||
if (existing?.diff !== undefined) return
|
||||
const pending = pendingSessionDiffFetches.get(key)
|
||||
if (pending) return pending
|
||||
}
|
||||
|
||||
const promise = (async () => {
|
||||
const instance = instances().get(instanceId)
|
||||
if (!instance?.client) return
|
||||
|
||||
const client = getRootClient(instanceId)
|
||||
|
||||
try {
|
||||
const diffs = await requestData<SnapshotFileDiff[]>(
|
||||
client.session.diff({ sessionID: sessionId, ...(await getSessionWorkspacePayload(instanceId, sessionId)) }),
|
||||
"session.diff",
|
||||
)
|
||||
|
||||
if (!Array.isArray(diffs)) {
|
||||
return
|
||||
}
|
||||
|
||||
withSession(instanceId, sessionId, (session) => {
|
||||
session.diff = diffs
|
||||
})
|
||||
} catch (error) {
|
||||
log.warn("Failed to fetch session diff", { instanceId, sessionId, error })
|
||||
}
|
||||
})()
|
||||
|
||||
pendingSessionDiffFetches.set(key, promise)
|
||||
void promise.finally(() => pendingSessionDiffFetches.delete(key))
|
||||
return promise
|
||||
}
|
||||
|
||||
interface SessionForkResponse {
|
||||
id: string
|
||||
title?: string
|
||||
|
|
@ -474,7 +432,6 @@ function toClientSessionV2(instanceId: string, apiSession: SessionV2Info, existi
|
|||
},
|
||||
metadata: existingSession?.metadata,
|
||||
revert: existingSession?.revert,
|
||||
diff: existingSession?.diff,
|
||||
pendingPermission: existingSession?.pendingPermission,
|
||||
pendingQuestion: existingSession?.pendingQuestion,
|
||||
}
|
||||
|
|
@ -851,10 +808,9 @@ async function fetchProviders(instanceId: string): Promise<void> {
|
|||
async function loadMessages(
|
||||
instanceId: string,
|
||||
sessionId: string,
|
||||
options?: { force?: boolean; skipDiff?: boolean; skipChildren?: boolean },
|
||||
options?: { force?: boolean; skipChildren?: boolean },
|
||||
): Promise<void> {
|
||||
const force = options?.force ?? false
|
||||
const skipDiff = options?.skipDiff ?? false
|
||||
const skipChildren = options?.skipChildren ?? false
|
||||
|
||||
if (force) {
|
||||
|
|
@ -896,12 +852,6 @@ async function loadMessages(
|
|||
throw new Error("Session not found")
|
||||
}
|
||||
|
||||
if (!skipDiff) {
|
||||
void loadSessionDiff(instanceId, sessionId).catch((error) => {
|
||||
log.warn("Failed to load session diff", { instanceId, sessionId, error })
|
||||
})
|
||||
}
|
||||
|
||||
setLoading((prev) => {
|
||||
const next = { ...prev }
|
||||
const loadingSet = next.loadingMessages.get(instanceId) || new Set()
|
||||
|
|
@ -1048,7 +998,7 @@ async function loadMessages(
|
|||
|
||||
if (!skipChildren && session.parentId === null) {
|
||||
for (const child of getDescendantSessions(instanceId, sessionId)) {
|
||||
void loadMessages(instanceId, child.id, { skipDiff: true, skipChildren: true }).catch((error) =>
|
||||
void loadMessages(instanceId, child.id, { skipChildren: true }).catch((error) =>
|
||||
log.error("Failed to load child session messages", {
|
||||
instanceId,
|
||||
sessionId: child.id,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import type {
|
|||
} from "../types/message"
|
||||
import type {
|
||||
EventSessionCompacted,
|
||||
EventSessionDiff,
|
||||
EventSessionError,
|
||||
EventSessionIdle,
|
||||
EventSessionUpdated,
|
||||
|
|
@ -563,31 +562,6 @@ function handleSessionUpdate(instanceId: string, event: EventSessionUpdated): vo
|
|||
}
|
||||
}
|
||||
|
||||
function handleSessionDiff(instanceId: string, event: EventSessionDiff): void {
|
||||
const sessionId = event.properties?.sessionID
|
||||
if (!sessionId) return
|
||||
|
||||
const diffs = event.properties?.diff
|
||||
if (!Array.isArray(diffs)) return
|
||||
|
||||
const existing = sessions().get(instanceId)?.get(sessionId)
|
||||
if (existing) {
|
||||
withSession(instanceId, sessionId, (session) => {
|
||||
session.diff = diffs
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// A diff event can arrive before we have hydrated the session list.
|
||||
// Best-effort: fetch the session record so the diff has somewhere to live.
|
||||
void (async () => {
|
||||
await fetchSessionInfo(instanceId, sessionId, (event as any)?.directory)
|
||||
withSession(instanceId, sessionId, (session) => {
|
||||
session.diff = diffs
|
||||
})
|
||||
})().catch((error) => log.warn("Failed to hydrate session for diff event", { instanceId, sessionId, error }))
|
||||
}
|
||||
|
||||
function handleSessionIdle(instanceId: string, event: EventSessionIdle): void {
|
||||
const sessionId = event.properties?.sessionID
|
||||
if (!sessionId) return
|
||||
|
|
@ -799,7 +773,6 @@ export {
|
|||
handleQuestionAsked,
|
||||
handleQuestionAnswered,
|
||||
handleSessionCompacted,
|
||||
handleSessionDiff,
|
||||
handleSessionError,
|
||||
handleSessionIdle,
|
||||
handleSessionStatus,
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ import {
|
|||
handleQuestionAnswered,
|
||||
handleQuestionAsked,
|
||||
handleSessionCompacted,
|
||||
handleSessionDiff,
|
||||
handleSessionError,
|
||||
handleSessionIdle,
|
||||
handleSessionStatus,
|
||||
|
|
@ -95,7 +94,6 @@ sseManager.onMessageRemoved = handleMessageRemoved
|
|||
sseManager.onMessagePartRemoved = handleMessagePartRemoved
|
||||
sseManager.onSessionUpdate = handleSessionUpdate
|
||||
sseManager.onSessionCompacted = handleSessionCompacted
|
||||
sseManager.onSessionDiff = handleSessionDiff
|
||||
sseManager.onSessionError = handleSessionError
|
||||
sseManager.onSessionIdle = handleSessionIdle
|
||||
sseManager.onSessionStatus = handleSessionStatus
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type {
|
|||
Provider as SDKProvider,
|
||||
Model as SDKModel,
|
||||
} from "@opencode-ai/sdk"
|
||||
import type { SessionStatus as SDKSessionStatus, SnapshotFileDiff } from "@opencode-ai/sdk/v2/client"
|
||||
import type { SessionStatus as SDKSessionStatus } from "@opencode-ai/sdk/v2/client"
|
||||
|
||||
// Export SDK types for external use
|
||||
export type {
|
||||
|
|
@ -77,7 +77,6 @@ export interface Session
|
|||
retry?: SessionRetryState | null // Retry metadata for transient backoff states
|
||||
idleSince?: number | null // Timestamp set when work finished but the session has not been viewed yet
|
||||
metadata?: Record<string, unknown> // Session metadata persisted by OpenCode
|
||||
diff?: SnapshotFileDiff[] // Session-level file diffs (hydrated via session.diff)
|
||||
}
|
||||
|
||||
// Adapter function to convert SDK Session to client Session
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue