fix(app): restore file tree layout

Restore the established tree rails while retaining file actions. Keep Escape local to generic editable fields so cancelling a file draft cannot interrupt an agent.
This commit is contained in:
Mohamed Boudra
2026-08-12 15:54:11 +02:00
parent 124e512b58
commit 474b4e70b4
9 changed files with 101 additions and 68 deletions
+13 -2
View File
@@ -553,8 +553,19 @@ test("changes diff switches between flat and tree file lists", async ({ page })
"none",
);
await expect(page.getByTestId("diff-file-0")).toBeVisible();
await expect(page.getByTestId("diff-folder-src-toggle").locator("svg")).toHaveCount(2);
await expect(page.getByTestId("diff-folder-src-toggle").locator("svg")).toHaveCount(1);
await expect(page.getByTestId("diff-file-0-toggle").locator("svg")).toHaveCount(1);
const folderToggleBounds = await page.getByTestId("diff-folder-src-toggle").boundingBox();
const folderChevronBounds = await page
.getByTestId("diff-folder-src-toggle")
.locator("svg")
.boundingBox();
expect(folderToggleBounds).not.toBeNull();
expect(folderChevronBounds).not.toBeNull();
expect(folderChevronBounds!.y + folderChevronBounds!.height / 2).toBeCloseTo(
folderToggleBounds!.y + folderToggleBounds!.height / 2,
0,
);
const folderLabelBounds = await page
.getByTestId("diff-folder-src")
.getByText("src", { exact: true })
@@ -565,7 +576,7 @@ test("changes diff switches between flat and tree file lists", async ({ page })
.boundingBox();
expect(folderLabelBounds).not.toBeNull();
expect(fileLabelBounds).not.toBeNull();
expect(fileLabelBounds!.x - folderLabelBounds!.x).toBeCloseTo(14, 0);
expect(fileLabelBounds!.x - folderLabelBounds!.x).toBeCloseTo(16, 0);
const folderToggle = page.getByTestId("diff-folder-src-toggle");
await folderToggle.click();
@@ -37,6 +37,7 @@ import {
import { seedWorkspace } from "../support/helpers/seed-client";
import { hasGithubAuth, createTempGithubRepo } from "../support/helpers/github-fixtures";
import { getServerId } from "../support/helpers/server-id";
import { openFileExplorer } from "../support/helpers/file-explorer";
const MINIMAL_PNG = Buffer.from(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
@@ -294,6 +295,28 @@ test.describe("Composer attachments", () => {
}
});
test("Escape cancels file creation without interrupting the running agent", async ({ page }) => {
test.setTimeout(120_000);
const agent = await startRunningMockAgent(page, {
prefix: "file-create-escape-",
model: "one-minute-stream",
prompt: "Stay running while a file draft is cancelled.",
});
try {
await openFileExplorer(page);
await page.getByTestId("files-new-file").click();
const nameInput = page.getByTestId("file-explorer-name-input");
await expect(nameInput).toBeVisible();
await nameInput.press("Escape");
await expect(nameInput).toBeHidden();
await expect(page.getByRole("button", { name: /stop|cancel/i }).first()).toBeVisible();
} finally {
await agent.cleanup();
}
});
test("composer is locked while new workspace agent is being created", async ({ page }) => {
test.setTimeout(120_000);
const serverId = getServerId();
@@ -124,6 +124,14 @@ test("creates, renames, copies, and deletes entries through the file explorer",
await page.getByTestId("files-new-folder").click();
await expect(nameInput).toBeVisible();
const draftRow = nameInput.locator("xpath=..");
const placeholderColor = await nameInput.evaluate(
(input) => getComputedStyle(input, "::placeholder").color,
);
const extraMutedChevronColor = await draftRow
.locator("svg")
.evaluate((icon) => getComputedStyle(icon).stroke);
expect(placeholderColor).toBe(extraMutedChevronColor);
await nameInput.press("Tab");
await expect(nameInput).toBeHidden();
@@ -189,18 +197,38 @@ test("creates, renames, copies, and deletes entries through the file explorer",
const fileRow = entry("created.txt").locator(
"xpath=ancestor::*[starts-with(@data-testid, 'file-explorer-row-')][1]",
);
await expect(folderRow.locator("svg")).toHaveCount(2);
await expect(folderRow.locator("svg")).toHaveCount(1);
await expect(fileRow.locator("svg")).toHaveCount(1);
const gitRow = entry(".git").locator(
"xpath=ancestor::*[starts-with(@data-testid, 'file-explorer-row-')][1]",
);
const collapsedChevronBounds = await gitRow.locator("svg").first().boundingBox();
const expandedChevronBounds = await folderRow.locator("svg").first().boundingBox();
const collapsedChevronSlotBounds = await gitRow
.locator("svg")
.first()
.locator("xpath=../..")
.boundingBox();
const expandedChevronSlotBounds = await folderRow
.locator("svg")
.first()
.locator("xpath=../..")
.boundingBox();
expect(collapsedChevronBounds).not.toBeNull();
expect(expandedChevronBounds).not.toBeNull();
expect(collapsedChevronSlotBounds).not.toBeNull();
expect(expandedChevronSlotBounds).not.toBeNull();
const collapsedChevronCenter = collapsedChevronBounds!.x + collapsedChevronBounds!.width / 2;
const expandedChevronCenter = expandedChevronBounds!.x + expandedChevronBounds!.width / 2;
expect(collapsedChevronCenter).toBeCloseTo(expandedChevronCenter, 0);
expect(collapsedChevronCenter).toBeCloseTo(
collapsedChevronSlotBounds!.x + collapsedChevronSlotBounds!.width / 2,
0,
);
expect(expandedChevronCenter).toBeCloseTo(
expandedChevronSlotBounds!.x + expandedChevronSlotBounds!.width / 2,
0,
);
const folderLabelBounds = await entry("folder").boundingBox();
const fileLabelBounds = await entry("created.txt").boundingBox();
expect(folderLabelBounds).not.toBeNull();