diff --git a/packages/app/src/components/session/session-preview-tab.tsx b/packages/app/src/components/session/session-preview-tab.tsx index 5e987f867..2da318a1a 100644 --- a/packages/app/src/components/session/session-preview-tab.tsx +++ b/packages/app/src/components/session/session-preview-tab.tsx @@ -36,7 +36,10 @@ function preprocessMarkdown(text: string): string { return text.replace(/```math\n([\s\S]*?)```/g, (_match, body: string) => `$$\n${body.trim()}\n$$`) } -export function SessionPreviewTab(props: { diffs: () => Array<{ file: string; status?: string }> }) { +export function SessionPreviewTab(props: { + diffs: () => Array<{ file: string; status?: string }> + touchedFiles?: () => Array<{ file: string; status: string }> +}) { const sdk = useSDK() const serverSDK = useServerSDK() const [selectedFile, setSelectedFile] = createSignal(undefined) @@ -48,24 +51,44 @@ export function SessionPreviewTab(props: { diffs: () => Array<{ file: string; st const zoomIn = () => setZoom((z) => Math.min(z + 10, 200)) const zoomOut = () => setZoom((z) => Math.max(z - 10, 50)) - // Filter diffs to only .md files + // Derive the file list from touchedFiles (tool-edit history, persists regardless of git state) + // supplemented by diffs for any files not already covered. const markdownFiles = createMemo((): PreviewFileEntry[] => { - return props - .diffs() - .filter((d) => d.file.endsWith(".md")) - .map((d) => { - const parts = d.file.split("/") - const basename = parts[parts.length - 1] - // relativePath: strip leading ~/ prefix - const relativePath = d.file.startsWith("~/") ? d.file.slice(2) : d.file - return { - path: d.file, - relativePath, - basename, - extension: ".md" as const, - changeType: (d.status === "added" ? "added" : "modified") as "added" | "modified", - } - }) + const seen = new Set() + const entries: PreviewFileEntry[] = [] + + const toEntry = (file: string, status: string): PreviewFileEntry | null => { + if (!file.endsWith(".md")) return null + if (seen.has(file)) return null + seen.add(file) + const parts = file.split("/") + const basename = parts[parts.length - 1] + const relativePath = file.startsWith("~/") ? file.slice(2) : file + return { + path: file, + relativePath, + basename, + extension: ".md" as const, + changeType: (status === "added" ? "added" : "modified") as "added" | "modified", + } + } + + // Primary: all files touched by edit tools in this session + const touched = props.touchedFiles?.() + if (touched) { + for (const t of touched) { + const entry = toEntry(t.file, t.status) + if (entry) entries.push(entry) + } + } + + // Supplement: any diff files not already in touchedFiles + for (const d of props.diffs()) { + const entry = toEntry(d.file, d.status === "added" ? "added" : "modified") + if (entry) entries.push(entry) + } + + return entries }) // Load file content when a file is selected diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 92c0adcff..cd9bd284e 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -739,6 +739,39 @@ export default function Page() { } return accumulateDiffs(editParts) }) + + // All files touched by edit tools in this session — independent of git state. + // The Preview tab uses this so documents remain visible even after being committed/reverted. + const touchedFiles = createMemo(() => { + const allMessages = messages() + if (!allMessages.length) return [] as Array<{ file: string; status: string }> + const home = typeof globalThis.process !== "undefined" ? globalThis.process.env?.HOME : undefined + const toHomePath = (p: string) => { + if (p.startsWith("~/")) return p + if (home && p.startsWith(home)) return "~" + p.slice(home.length) + return p + } + const seen = new Map() + for (const msg of allMessages) { + const msgParts = sync().data.part[msg.id] + if (!msgParts) continue + for (const part of msgParts) { + if (part.type !== "tool" || !EDIT_TOOLS.has(part.tool)) continue + if (part.state.status !== "completed") continue + const meta = part.state.metadata as Record | undefined + const filediff = meta?.filediff as { file?: string } | undefined + if (filediff?.file) { + const normalized = toHomePath(filediff.file) + if (!seen.has(normalized)) { + // First touch of this file — check if it was created or modified + seen.set(normalized, part.tool === "write" ? "added" : "modified") + } + } + } + } + return Array.from(seen, ([file, status]) => ({ file, status })) + }) + const activeReviewFile = () => { const diffs = reviewDiffs() const selected = reviewFile() @@ -2259,6 +2292,7 @@ export default function Page() { focusReviewDiff={focusReviewDiff} reviewSnap={ui.reviewSnap} size={size} + touchedFiles={touchedFiles} /> @@ -2311,6 +2345,7 @@ export default function Page() { reviewSnap={ui.reviewSnap} size={size} stacked={desktopV2PanelLayout().stacked} + touchedFiles={touchedFiles} /> diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 7e69f7635..fefa143e1 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -82,6 +82,7 @@ export function SessionSidePanel(props: { reviewSnap: boolean size: Sizing stacked?: boolean + touchedFiles?: () => Array<{ file: string; status: string }> }) { const layout = useLayout() const settings = useSettings() @@ -245,9 +246,13 @@ export function SessionSidePanel(props: { return active !== "review" && active !== "context" && active !== "empty" && active !== SESSION_PREVIEW_TAB }) - // Markdown files for the Preview tab (only shown when at least one .md file exists) - const markdownDiffs = createMemo(() => diffs().filter((d) => d.file.endsWith(".md"))) - const hasMarkdownFiles = createMemo(() => markdownDiffs().length > 0) + // Markdown files for the Preview tab — check both git diffs and tool-edit history + const hasMarkdownFiles = createMemo(() => { + if (diffs().some((d) => d.file.endsWith(".md"))) return true + const touched = props.touchedFiles?.() + if (touched?.some((t) => t.file.endsWith(".md"))) return true + return false + }) // Panel menu items const panelMenuItems = createMemo((): PanelMenuItem[] => [ @@ -528,7 +533,7 @@ export function SessionSidePanel(props: {
- +
@@ -737,11 +742,11 @@ export function SessionSidePanel(props: { - -
- -
-
+ +
+ +
+