diff --git a/frontend/src/lib/components/copilot/chat/ToolDiffCard.svelte b/frontend/src/lib/components/copilot/chat/ToolDiffCard.svelte
index b360dc3ac5..94e33374c3 100644
--- a/frontend/src/lib/components/copilot/chat/ToolDiffCard.svelte
+++ b/frontend/src/lib/components/copilot/chat/ToolDiffCard.svelte
@@ -13,9 +13,10 @@
interface Props {
message: ToolDisplayMessage
+ hidePreviewChip?: boolean
}
- let { message }: Props = $props()
+ let { message, hidePreviewChip = false }: Props = $props()
const diff = $derived(toolCodeDiff(message))
const isStreaming = $derived(Boolean(message.isStreamingArguments))
@@ -56,7 +57,7 @@
const isRunning = $derived(Boolean(message.isLoading && !message.needsConfirmation))
const showPreviewChip = $derived(
- Boolean(message.previewCard && !message.isLoading && !message.error)
+ Boolean(!hidePreviewChip && message.previewCard && !message.isLoading && !message.error)
)
diff --git a/frontend/src/lib/components/copilot/chat/ToolExecutionDisplay.svelte b/frontend/src/lib/components/copilot/chat/ToolExecutionDisplay.svelte
index 15a7e2ea66..7e92c55c89 100644
--- a/frontend/src/lib/components/copilot/chat/ToolExecutionDisplay.svelte
+++ b/frontend/src/lib/components/copilot/chat/ToolExecutionDisplay.svelte
@@ -197,7 +197,7 @@
{:else if isRunCard}
{:else if isDiffCard}
-
+
{:else if planState}
diff --git a/frontend/src/lib/components/copilot/chat/toolGroups.test.ts b/frontend/src/lib/components/copilot/chat/toolGroups.test.ts
index a4fc4ce0a8..4dd3b376fe 100644
--- a/frontend/src/lib/components/copilot/chat/toolGroups.test.ts
+++ b/frontend/src/lib/components/copilot/chat/toolGroups.test.ts
@@ -187,6 +187,19 @@ describe('groupToolRuns', () => {
).toEqual([{ edit: [0, 1, 2] }, { edit: [3, 4] }])
})
+ it('folds app edits that carry a file diff, but not a script edit with one', () => {
+ const app = { path: 'f/a/x' }
+ const diff = { codeDiff: { before: 'a', after: 'b' } }
+ expect(
+ shape([
+ tool('init_app', app),
+ tool('write_app_file', { ...app, file_path: '/App.tsx' }, diff),
+ tool('delete_app_runnable', { ...app, key: 'greet' }, diff),
+ tool('write_script', { path: 'f/a/s' }, diff)
+ ])
+ ).toEqual([{ edit: [0, 1, 2] }, 3])
+ })
+
it('leaves a flow read that prepares an edit to the edit group, not the lookups before it', () => {
const flow = { type: 'flow', path: 'f/a/flow' }
expect(
diff --git a/frontend/src/lib/components/copilot/chat/toolGroups.ts b/frontend/src/lib/components/copilot/chat/toolGroups.ts
index 94936d4293..f3e3ad0aa9 100644
--- a/frontend/src/lib/components/copilot/chat/toolGroups.ts
+++ b/frontend/src/lib/components/copilot/chat/toolGroups.ts
@@ -116,7 +116,9 @@ function groupableCall(message: DisplayMessage): ToolDisplayMessage | undefined
if (message.needsConfirmation || message.blockedByPlanMode) return undefined
if (message.heldForFolderInstructions) return undefined
if (message.runForm || message.inspectedRun || message.userQuestion) return undefined
- if (message.codeDiff || message.imageUrl || message.webSearchSources) return undefined
+ // An app edit's diff covers one file of many, so it stays a row inside the app's group.
+ if (message.codeDiff && EDIT_TOOLS[message.toolName] !== 'app') return undefined
+ if (message.imageUrl || message.webSearchSources) return undefined
if (!message.error && webSearchResultOf(message.result)) return undefined
// The user's own refusal is a decision, not a failure to fold away.
if (message.declinedByUser) return undefined