Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 41 additions & 18 deletions packages/app/src/components/session/session-preview-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined>(undefined)
Expand All @@ -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<string>()
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
Expand Down
35 changes: 35 additions & 0 deletions packages/app/src/pages/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>()
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<string, unknown> | 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()
Expand Down Expand Up @@ -2259,6 +2292,7 @@ export default function Page() {
focusReviewDiff={focusReviewDiff}
reviewSnap={ui.reviewSnap}
size={size}
touchedFiles={touchedFiles}
/>
</Suspense>
</Show>
Expand Down Expand Up @@ -2311,6 +2345,7 @@ export default function Page() {
reviewSnap={ui.reviewSnap}
size={size}
stacked={desktopV2PanelLayout().stacked}
touchedFiles={touchedFiles}
/>
</Suspense>
</div>
Expand Down
23 changes: 14 additions & 9 deletions packages/app/src/pages/session/session-side-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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[] => [
Expand Down Expand Up @@ -528,7 +533,7 @@ export function SessionSidePanel(props: {
<Show when={activeTab() === SESSION_PREVIEW_TAB}>
<Tabs.Content value={SESSION_PREVIEW_TAB} class="flex flex-col h-full overflow-hidden contain-strict">
<div class="relative flex-1 min-h-0 overflow-hidden">
<SessionPreviewTab diffs={diffs} />
<SessionPreviewTab diffs={diffs} touchedFiles={props.touchedFiles} />
</div>
</Tabs.Content>
</Show>
Expand Down Expand Up @@ -737,11 +742,11 @@ export function SessionSidePanel(props: {
</Show>

<Show when={activeTab() === SESSION_PREVIEW_TAB}>
<Tabs.Content value={SESSION_PREVIEW_TAB} class="flex flex-col h-full overflow-hidden contain-strict">
<div class="relative flex-1 min-h-0 overflow-hidden">
<SessionPreviewTab diffs={diffs} />
</div>
</Tabs.Content>
<Tabs.Content value={SESSION_PREVIEW_TAB} class="flex flex-col h-full overflow-hidden contain-strict">
<div class="relative flex-1 min-h-0 overflow-hidden">
<SessionPreviewTab diffs={diffs} touchedFiles={props.touchedFiles} />
</div>
</Tabs.Content>
</Show>

<Show when={fileBrowserMounted()}>
Expand Down
Loading