diff --git a/crates/diffcore-tauri/ui/tests/e2e/activity-stream.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/activity-stream.spec.ts index 2d045ffa..21974095 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/activity-stream.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/activity-stream.spec.ts @@ -169,11 +169,16 @@ test.describe("AI activity stream", () => { const panel = page.locator(".panel-right"); const logPanel = page.getByTestId("activity-log-panel"); const latestCard = page.getByTestId("activity-entry").last(); - const inspector = page.getByTestId("activity-inspector"); await latestCard.click(); - await expect(inspector).toContainText("stdout.command_execution"); - await expect(inspector).toContainText("\"path\": \"crates/diffcore-tauri/ui/src/App.tsx\""); + // The dedicated inspector was replaced by inline hint chips on the card; + // the event type and payload are exposed via the chips' title attributes. + await expect( + latestCard.locator('.activity-card-hint[title="stdout.command_execution"]'), + ).toBeVisible(); + await expect( + latestCard.locator(".activity-card-hint", { hasText: "Payload" }), + ).toHaveAttribute("title", /"path": "crates\/diffcore-tauri\/ui\/src\/App\.tsx"/); await expect(page.getByTestId("activity-panel")).toContainText("Latest stream captured from Codex CLI"); for (const locator of [panel, logPanel, page.locator(".activity-view-switch")]) { @@ -230,10 +235,14 @@ test.describe("AI activity stream", () => { await expect(cards.first()).toContainText("Preparing refinement request"); await expect(cards.last()).toContainText("Refinement rationale"); - const heights = await cards.evaluateAll((elements) => - elements.map((element) => Math.round(element.getBoundingClientRect().height)), + // compact cards: not collapsed, not clipping (overflow: hidden) + const metrics = await cards.evaluateAll((elements) => + elements.map((element) => [element.clientHeight, element.scrollHeight]), ); - expect(heights.every((height) => height >= 78)).toBeTruthy(); + for (const [clientHeight, scrollHeight] of metrics) { + expect(clientHeight).toBeGreaterThanOrEqual(40); + expect(clientHeight).toBeGreaterThanOrEqual(scrollHeight - 1); + } }); test("surfaces the effective Codex backend in Settings when local auth is available", async ({ page }) => { diff --git a/crates/diffcore-tauri/ui/tests/e2e/bugfixes.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/bugfixes.spec.ts index cd29237b..92598fac 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/bugfixes.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/bugfixes.spec.ts @@ -127,8 +127,10 @@ test.describe("Bugfix — Keyboard shortcuts with Monaco focus", () => { // Replay bar should appear await expect(page.locator(".replay-bar")).toBeVisible(); - // Exit replay - await page.keyboard.press("Escape"); + // Exit replay — r toggles replay off. (Escape is deliberately passed + // through to Monaco while the editor has focus, so r is the keyboard + // exit path from Monaco focus.) + await page.keyboard.press("r"); await page.waitForTimeout(300); await expect(page.locator(".replay-bar")).not.toBeVisible(); }); @@ -153,6 +155,9 @@ test.describe("Bugfix — Flow graph fullscreen", () => { test.beforeEach(async ({ page }) => { await page.goto("/"); await waitForAnalysis(page); + // The flow graph now lives under the Graph subtab in the right panel + await page.locator(".annotation-subtab", { hasText: "Graph" }).click(); + await expect(page.locator("[data-testid='flow-graph']")).toBeVisible(); }); test("07 — fullscreen re-centers the graph (not stuck in top-left)", async ({ page }) => { diff --git a/crates/diffcore-tauri/ui/tests/e2e/comment-strip-v2.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/comment-strip-v2.spec.ts index 0053b028..a520e6ab 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/comment-strip-v2.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/comment-strip-v2.spec.ts @@ -1,13 +1,12 @@ /** - * Comment Strip v2 — Playwright E2E tests. + * Comments v2 — Playwright E2E tests. * * Covers: * - File comment icon in left panel (clickable, with count) - * - Comment strip collapse/expand animation - * - Active comment highlighting in nav pills - * - File path shown on comment cards - * - Scrollability of comment nav and detail when many comments - * - Smooth transitions on collapsible sections + * - Clicking the file comment icon opens the right-panel comments tab + * - Active comment highlighting on comment cards + * - File path shown on comment file-group headers + * - Scrollability of the comments list with many comments */ import { test, expect, type Page } from "@playwright/test"; @@ -29,6 +28,10 @@ async function addCommentViaUI(page: Page, text: string) { await page.waitForTimeout(300); } +async function openCommentsTab(page: Page) { + await page.getByTestId("comments-tab").click(); +} + // ── File Comment Icon ── test.describe("File Comment Icon", () => { @@ -57,67 +60,19 @@ test.describe("File Comment Icon", () => { await expect(count).toContainText("2"); }); - test("03 — clicking comment icon opens comment strip", async ({ page }) => { + test("03 — clicking comment icon opens the comments tab", async ({ page }) => { await addCommentViaUI(page, "A comment"); - // Collapse the comments strip first - const toggle = page.locator(".comment-strip-toggle"); - await toggle.click(); - await page.waitForTimeout(300); - - // Verify collapsed - const body = page.locator(".comment-strip-body"); - await expect(body).toHaveCSS("max-height", "0px"); + // Comments tab is not active by default (annotations is) + await expect(page.getByTestId("comments-tab")).toHaveAttribute("aria-selected", "false"); // Click the file comment icon await page.locator(".file-comment-btn").first().click(); await page.waitForTimeout(400); - // Comments strip should be expanded - await expect(page.locator(".comment-strip-collapsed")).not.toBeVisible(); - }); -}); - -// ── Comment Strip Collapse/Expand ── - -test.describe("Comment Strip Collapse", () => { - test.beforeEach(async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - await addCommentViaUI(page, "Test comment for collapse"); - }); - - test("04 — comment strip has 'Comments' header with count", async ({ page }) => { - const toggle = page.locator(".comment-strip-toggle"); - await expect(toggle).toBeVisible(); - await expect(toggle).toContainText("Comments"); - - const count = page.locator(".comment-strip-count").first(); - await expect(count).toContainText("1"); - }); - - test("05 — clicking header collapses the comment body", async ({ page }) => { - const toggle = page.locator(".comment-strip-toggle"); - await toggle.click(); - await page.waitForTimeout(400); - - // Body should be collapsed (max-height: 0) - const strip = page.locator(".comment-strip"); - await expect(strip).toHaveClass(/comment-strip-collapsed/); - }); - - test("06 — clicking header again expands the comment body", async ({ page }) => { - const toggle = page.locator(".comment-strip-toggle"); - - // Collapse - await toggle.click(); - await page.waitForTimeout(400); - await expect(page.locator(".comment-strip")).toHaveClass(/comment-strip-collapsed/); - - // Expand - await toggle.click(); - await page.waitForTimeout(400); - await expect(page.locator(".comment-strip")).not.toHaveClass(/comment-strip-collapsed/); + // Right panel should switch to the comments tab and show the comment + await expect(page.getByTestId("comments-tab")).toHaveAttribute("aria-selected", "true"); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); }); }); @@ -129,36 +84,29 @@ test.describe("Active Comment Highlight", () => { await waitForAnalysis(page); await addCommentViaUI(page, "First comment"); await addCommentViaUI(page, "Second comment"); + await openCommentsTab(page); }); - test("07 — clicking nav pill highlights it as active", async ({ page }) => { - const firstPill = page.locator(".comment-strip-nav-item").first(); - await firstPill.click(); + test("04 — clicking a comment card highlights it as active", async ({ page }) => { + const firstCard = page.locator(".comments-tab-card").first(); + await firstCard.click(); await page.waitForTimeout(300); - await expect(firstPill).toHaveClass(/comment-strip-nav-active/); + await expect(firstCard).toHaveClass(/comments-tab-card-active/); }); - test("08 — clicking a different pill moves the active highlight", async ({ page }) => { - const firstPill = page.locator(".comment-strip-nav-item").first(); - const secondPill = page.locator(".comment-strip-nav-item").nth(1); - - await firstPill.click(); - await page.waitForTimeout(300); - await expect(firstPill).toHaveClass(/comment-strip-nav-active/); - - await secondPill.click(); - await page.waitForTimeout(300); - await expect(secondPill).toHaveClass(/comment-strip-nav-active/); - await expect(firstPill).not.toHaveClass(/comment-strip-nav-active/); - }); + test("05 — clicking a different card moves the active highlight", async ({ page }) => { + const firstCard = page.locator(".comments-tab-card").first(); + const secondCard = page.locator(".comments-tab-card").nth(1); - test("09 — clicking a comment card highlights it as active", async ({ page }) => { - const firstCard = page.locator(".comment-strip-item").first(); await firstCard.click(); await page.waitForTimeout(300); + await expect(firstCard).toHaveClass(/comments-tab-card-active/); - await expect(firstCard).toHaveClass(/comment-strip-item-active/); + await secondCard.click(); + await page.waitForTimeout(300); + await expect(secondCard).toHaveClass(/comments-tab-card-active/); + await expect(firstCard).not.toHaveClass(/comments-tab-card-active/); }); }); @@ -170,10 +118,11 @@ test.describe("File Path on Comments", () => { await waitForAnalysis(page); }); - test("10 — comment card shows file path", async ({ page }) => { + test("06 — comment file group shows file path", async ({ page }) => { await addCommentViaUI(page, "Comment with path"); + await openCommentsTab(page); - const filepath = page.locator(".comment-strip-filepath").first(); + const filepath = page.locator(".comments-tab-file-path").first(); await expect(filepath).toBeVisible(); // Should contain a file name (not empty) const text = await filepath.textContent(); @@ -183,83 +132,27 @@ test.describe("File Path on Comments", () => { // ── Scrollability ── -test.describe("Comment Strip Scrollability", () => { +test.describe("Comments List Scrollability", () => { test.beforeEach(async ({ page }) => { await page.goto("/"); await waitForAnalysis(page); }); - test("11 — comment detail panel is scrollable with many comments", async ({ page }) => { + test("07 — comments list is scrollable with many comments", async ({ page }) => { // Add many comments to overflow the panel - for (let i = 0; i < 8; i++) { + for (let i = 0; i < 10; i++) { await addCommentViaUI(page, `Comment number ${i + 1} — this is a longer comment to take up vertical space in the panel`); } + await openCommentsTab(page); - const detail = page.locator(".comment-strip-detail"); - await expect(detail).toBeVisible(); + const list = page.locator(".comments-tab-list"); + await expect(list).toBeVisible(); // Check that overflow-y is set to auto - await expect(detail).toHaveCSS("overflow-y", "auto"); + await expect(list).toHaveCSS("overflow-y", "auto"); - // The detail panel should have scroll height larger than client height - const hasScroll = await detail.evaluate((el) => el.scrollHeight > el.clientHeight); + // The list should have scroll height larger than client height + const hasScroll = await list.evaluate((el) => el.scrollHeight > el.clientHeight); expect(hasScroll).toBe(true); }); - - test("12 — comment nav panel is scrollable with many comments", async ({ page }) => { - for (let i = 0; i < 8; i++) { - await addCommentViaUI(page, `Nav scroll test ${i + 1}`); - } - - const nav = page.locator(".comment-strip-nav"); - await expect(nav).toBeVisible(); - await expect(nav).toHaveCSS("overflow-y", "auto"); - }); -}); - -// ── Collapsible Sections Transitions ── - -test.describe("Smooth Collapsible Sections", () => { - test.beforeEach(async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - }); - - test("13 — flow graph section uses collapsible-body with transition", async ({ page }) => { - const body = page.locator(".flow-graph-section .collapsible-body"); - if (await body.isVisible()) { - // Should have transition property set - const transition = await body.evaluate((el) => getComputedStyle(el).transition); - expect(transition).toContain("max-height"); - } - }); - - test("14 — edges section uses collapsible-body with transition", async ({ page }) => { - const body = page.locator(".edges-section .collapsible-body"); - if (await body.isVisible()) { - const transition = await body.evaluate((el) => getComputedStyle(el).transition); - expect(transition).toContain("max-height"); - } - }); - - test("15 — edges section starts collapsed", async ({ page }) => { - const section = page.locator(".edges-section"); - if (await section.isVisible()) { - await expect(section).toHaveClass(/edges-collapsed/); - } - }); - - test("16 — edges section expands on toggle click", async ({ page }) => { - const toggle = page.locator(".edges-section .section-toggle"); - if (await toggle.isVisible()) { - await toggle.click(); - await page.waitForTimeout(400); - - const section = page.locator(".edges-section"); - await expect(section).not.toHaveClass(/edges-collapsed/); - - // Edge items should be visible - await expect(page.locator(".edge-item").first()).toBeVisible(); - } - }); }); diff --git a/crates/diffcore-tauri/ui/tests/e2e/gutter-comments.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/gutter-comments.spec.ts index bda0635f..6fadd38c 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/gutter-comments.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/gutter-comments.spec.ts @@ -110,10 +110,6 @@ test.describe("Gutter Comment Icons", () => { console.log("Comments after add:", JSON.stringify(info)); expect(info.total).toBeGreaterThanOrEqual(1); - - // Check for comment strip - const stripVisible = await page.locator(".comment-strip").isVisible(); - console.log("Comment strip visible:", stripVisible); }); test("04 — glyph icon appears in editor after code comment added", async ({ page }) => { @@ -175,7 +171,7 @@ test.describe("Gutter Comment Icons", () => { expect(highlights).toBe(4); }); - test("08 — glyph click activates comment in strip and scrolls it into view", async ({ page }) => { + test("08 — glyph click activates comment in comments tab and scrolls it into view", async ({ page }) => { // Add two comments so we can verify the right one gets activated await addCodeCommentViaUI(page, "First comment on lines 5-8"); @@ -203,24 +199,23 @@ test.describe("Gutter Comment Icons", () => { const glyphs = await page.locator(".comment-glyph-icon").count(); expect(glyphs).toBe(2); - // Comment strip should show 2 items - const items = await page.locator(".comment-strip-item").count(); - expect(items).toBe(2); + // Comments tab badge should show 2 — checked without opening the tab, so the + // glyph-click activation assert below stays meaningful + await expect(page.getByTestId("comments-tab").locator(".panel-tab-count")).toHaveText("2"); // Click the first glyph icon const firstGlyph = page.locator(".comment-glyph-icon").first(); await firstGlyph.click(); await page.waitForTimeout(500); - // The first comment card should be highlighted as active - const activeItems = page.locator(".comment-strip-item-active"); + // Glyph click activates the comments tab and highlights the matching card + await expect(page.getByTestId("comments-tab")).toHaveAttribute("aria-selected", "true"); + const activeItems = page.locator(".comments-tab-card-active"); expect(await activeItems.count()).toBe(1); - - // The comments strip should be expanded (not collapsed) - await expect(page.locator(".comment-strip")).not.toHaveClass(/comment-strip-collapsed/); + await expect(activeItems.first()).toContainText("First comment on lines 5-8"); // Take screenshot for verification - await page.locator(".panel-center").screenshot({ + await page.screenshot({ path: path.join(SCREENSHOTS_DIR, "87-glyph-click-activates-comment.png"), }); }); diff --git a/crates/diffcore-tauri/ui/tests/e2e/hardening.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/hardening.spec.ts index c912d1af..02f1f802 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/hardening.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/hardening.spec.ts @@ -374,7 +374,8 @@ test.describe("Hardening — LLM Controls", () => { await expect(page.locator(".settings-panel h3").filter({ hasText: "Refinement" })).toBeVisible(); await expect(page.locator(".settings-panel h3").filter({ hasText: "Exclude Paths" })).toBeVisible(); await expect(page.locator(".settings-panel label").filter({ hasText: "Primary backend" })).toBeVisible(); - await expect(page.locator(".settings-panel label").filter({ hasText: "Model" })).toBeVisible(); + // Both AI Access and Refinement sections have a "Model" label now + await expect(page.locator(".settings-panel label").filter({ hasText: "Model" }).first()).toBeVisible(); }); test("23 — settings panel: API key configured (green indicator)", async ({ page }) => { @@ -479,11 +480,13 @@ test.describe("Hardening — LLM Annotations", () => { await page.goto("/"); await waitForAnalysis(page); - // Click summarize + // Click summarize — the right panel switches to the LLM activity stream await page.locator(".btn-summarize").click(); - // Brief wait for mock delay - await page.waitForTimeout(1200); - await page.getByRole("tab", { name: "Annotations" }).click(); + // Wait for the mock activity job to start and finish + await expect(page.locator(".annotation-section.llm-loading:not(.llm-setup-cta)")).toBeVisible(); + await expect(page.locator(".annotation-section.llm-loading:not(.llm-setup-cta)")).toBeHidden({ timeout: 15_000 }); + // Results render in the Info tab of the right panel + await page.getByRole("tab", { name: "Info" }).click(); // Verify LLM overview rendered await expect(page.locator(".llm-summary").first()).toBeVisible(); @@ -504,10 +507,13 @@ test.describe("Hardening — LLM Annotations", () => { await page.goto("/"); await waitForAnalysis(page); - // Click "Analyze This Flow" + // Click "Analyze This Flow" — the right panel switches to the activity stream await page.locator(".btn-analyze-flow").click(); - await page.waitForTimeout(1000); - await page.getByRole("tab", { name: "Annotations" }).click(); + // Wait for the mock activity job to start and finish + await expect(page.locator(".annotation-section.llm-loading:not(.llm-setup-cta)")).toBeVisible(); + await expect(page.locator(".annotation-section.llm-loading:not(.llm-setup-cta)")).toBeHidden({ timeout: 15_000 }); + // Results render in the Info tab of the right panel + await page.getByRole("tab", { name: "Info" }).click(); // Verify deep analysis rendered await expect(page.locator(".llm-narrative")).toBeVisible(); diff --git a/crates/diffcore-tauri/ui/tests/e2e/review-comments.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/review-comments.spec.ts index 37bfad05..f5095c64 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/review-comments.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/review-comments.spec.ts @@ -85,9 +85,11 @@ test.describe("Review Comments", () => { // Toast should show "Comment saved" await expect(page.locator(".toast")).toContainText("Comment saved"); - const commentStripItem = page.locator(".comment-strip-item").first(); - await expect(commentStripItem).toBeVisible(); - await expect(commentStripItem.locator(".comment-strip-text")).toContainText("This needs more validation"); + // Comment should appear in the right-panel comments tab + await page.getByTestId("comments-tab").click(); + const card = page.locator(".comments-tab-card").first(); + await expect(card).toBeVisible(); + await expect(card.locator(".comments-tab-card-text")).toContainText("This needs more validation"); }); test("04 — comment count badge shows on group with comments", async ({ page }) => { @@ -114,27 +116,30 @@ test.describe("Review Comments", () => { // Add second comment (still file-level) await addCommentViaUI(page, "Second comment"); - const commentStripItems = page.locator(".comment-strip-item"); - await expect(commentStripItems).toHaveCount(2); + await page.getByTestId("comments-tab").click(); + const cards = page.locator(".comments-tab-card"); + await expect(cards).toHaveCount(2); - const firstItem = commentStripItems.nth(0); - await expect(firstItem.locator(".comment-strip-badge")).toHaveText("file"); - await expect(firstItem.locator(".comment-strip-text")).toContainText("First comment"); + const firstCard = cards.nth(0); + await expect(firstCard.locator(".comment-strip-badge")).toHaveText("file"); + await expect(firstCard.locator(".comments-tab-card-text")).toContainText("First comment"); - const secondItem = commentStripItems.nth(1); - await expect(secondItem.locator(".comment-strip-text")).toContainText("Second comment"); + const secondCard = cards.nth(1); + await expect(secondCard.locator(".comments-tab-card-text")).toContainText("Second comment"); }); test("07 — delete comment via X button", async ({ page }) => { await addCommentViaUI(page, "To be deleted"); - await expect(page.locator(".comment-strip-item")).toHaveCount(1); + + await page.getByTestId("comments-tab").click(); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); // Click delete button await page.locator(".comment-strip-delete").click(); await page.waitForTimeout(300); // Comment should be gone - await expect(page.locator(".comment-strip-item")).toHaveCount(0); + await expect(page.locator(".comments-tab-empty")).toBeVisible(); }); test("08 — copy comments button copies all comments to clipboard", async ({ page, context }) => { @@ -231,8 +236,8 @@ test.describe("Review Comments", () => { // Add a comment to the first group await addCommentViaUI(page, "Persistent comment"); - // Verify comment is there - await expect(page.locator(".comment-strip-item")).toHaveCount(1); + // Verify comment count shows on the comments tab + await expect(page.getByTestId("comments-tab").locator(".panel-tab-count")).toHaveText("1"); // Switch to next group (J = next group) await page.keyboard.press("J"); @@ -249,8 +254,9 @@ test.describe("Review Comments", () => { // Verify we're back on the first group await expect(page.locator(".group-detail-name")).toHaveText(firstGroupName!); - // Comment should still be there - await expect(page.locator(".comment-strip-item")).toHaveCount(1); - await expect(page.locator(".comment-strip-text")).toContainText("Persistent comment"); + // Comment should still be there in the comments tab + await page.getByTestId("comments-tab").click(); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); + await expect(page.locator(".comments-tab-card-text")).toContainText("Persistent comment"); }); }); diff --git a/crates/diffcore-tauri/ui/tests/e2e/screenshots.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/screenshots.spec.ts index 1d7f7d17..f52fd3e3 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/screenshots.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/screenshots.spec.ts @@ -43,93 +43,25 @@ test.describe("Screenshots", () => { }); }); - test("61 — comment strip with comments", async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - - // Add two comments - await addCommentViaUI(page, "This validation logic needs error boundaries"); - await addCommentViaUI(page, "Consider extracting this into a shared utility"); - await page.waitForTimeout(500); - - // Focus on center panel to show the strip - await page.locator(".panel-center").screenshot({ - path: path.join(SCREENSHOTS_DIR, "61-comment-strip.png"), - }); - }); + // Tests 61 and 63-66 were deleted: the comment strip is dead code, the edges + // toggle moved to the Edges subtab (covered in ui-improvements.spec.ts), and + // the graph captures are owned by visual-polish (11-flow-graph.png) and + // hardening (37-graph-fullscreen.png). - test("62 — comment strip close-up", async ({ page }) => { + test("62 — comments tab close-up", async ({ page }) => { await page.goto("/"); await waitForAnalysis(page); await addCommentViaUI(page, "Potential SQL injection here — use parameterized queries"); await page.waitForTimeout(500); - const strip = page.locator(".comment-strip"); - await expect(strip).toBeVisible(); - await strip.screenshot({ - path: path.join(SCREENSHOTS_DIR, "62-comment-strip-closeup.png"), - }); - }); - - test("63 — edges section collapsed (default)", async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - - // Scroll the right panel to show edges toggle - const edgesToggle = page.locator(".annotation-section .flow-graph-toggle").filter({ hasText: "Edges" }); - if (await edgesToggle.isVisible()) { - await edgesToggle.scrollIntoViewIfNeeded(); - await page.waitForTimeout(300); - await page.locator(".panel-right").screenshot({ - path: path.join(SCREENSHOTS_DIR, "63-edges-collapsed.png"), - }); - } - }); - - test("64 — edges section expanded", async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - - const edgesToggle = page.locator(".annotation-section .flow-graph-toggle").filter({ hasText: "Edges" }); - if (await edgesToggle.isVisible()) { - await edgesToggle.click(); - await page.waitForTimeout(300); - await edgesToggle.scrollIntoViewIfNeeded(); - await page.locator(".panel-right").screenshot({ - path: path.join(SCREENSHOTS_DIR, "64-edges-expanded.png"), - }); - } - }); - - test("65 — flow graph (normal view)", async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - - const graph = page.locator("[data-testid='flow-graph']"); - if (await graph.isVisible()) { - await graph.screenshot({ - path: path.join(SCREENSHOTS_DIR, "65-flow-graph.png"), - }); - } - }); - - test("66 — flow graph fullscreen", async ({ page }) => { - await page.goto("/"); - await waitForAnalysis(page); - - const fsBtn = page.locator(".flow-fullscreen-btn"); - if (await fsBtn.isVisible()) { - await fsBtn.click(); - await page.waitForTimeout(1000); - - await page.screenshot({ - path: path.join(SCREENSHOTS_DIR, "66-flow-graph-fullscreen.png"), - fullPage: false, - }); + await page.getByTestId("comments-tab").click(); - await page.keyboard.press("Escape"); - } + const tab = page.locator(".comments-tab"); + await expect(tab.locator(".comments-tab-card")).toHaveCount(1); + await tab.screenshot({ + path: path.join(SCREENSHOTS_DIR, "62-comments-tab-closeup.png"), + }); }); test("67 — open-with dropdown with icons", async ({ page }) => { diff --git a/crates/diffcore-tauri/ui/tests/e2e/source-explorer.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/source-explorer.spec.ts index 8618c23c..2a0200cb 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/source-explorer.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/source-explorer.spec.ts @@ -24,19 +24,23 @@ test.describe("Source Explorer", () => { await expect(page.locator(".source-outline-section-header").filter({ hasText: "Operations" })).toBeVisible(); await expect(page.locator(".source-outline-section-header").filter({ hasText: "Dependencies" })).toBeVisible(); await expect(page.locator(".source-outline-item").filter({ hasText: "POST /api/users" })).toBeVisible(); - await expect(page.locator(".source-editor-surface .monaco-editor")).toBeVisible(); + // The outline lives in the right panel; the Monaco diff editor stays in the center panel + await expect(page.locator(".panel-center").getByRole("code").first()).toBeVisible(); }); test("02 — clicking a file symbol updates the native editor context", async ({ page }) => { await page.locator(".file-item").filter({ hasText: "services/user-service.ts" }).click(); await openSourceView(page); + // The outline header shows the file being inspected + await expect(page.locator(".source-outline-file")).toContainText("services/user-service.ts"); + const symbol = page.locator(".source-outline-item").filter({ hasText: "UserService.create" }); await symbol.click(); - await expect(page.locator(".source-editor-title")).toContainText("UserService.create"); - await expect(page.locator(".source-editor-subtitle")).toContainText("Fn"); + // The clicked symbol becomes the active outline item and is marked as a function await expect(symbol).toHaveClass(/active/); + await expect(symbol.locator(".source-outline-kind")).toHaveText("Fn"); }); test("03 — interface-heavy files render interfaces and types natively", async ({ page }) => { @@ -58,7 +62,11 @@ test.describe("Source Explorer", () => { await expect(page.locator(".file-item.selected")).toContainText("repositories/user-repo.ts"); await expect(page.locator(".source-explorer")).toBeVisible(); - await expect(page.locator(".source-editor-title")).toContainText("UserRepository.insert"); + // The outline now shows the linked file, with the target symbol focused + await expect(page.locator(".source-outline-file")).toContainText("repositories/user-repo.ts"); + await expect( + page.locator(".source-outline-item.active").filter({ hasText: "UserRepository.insert" }), + ).toBeVisible(); }); test("05 — source explorer surfaces stay on the dark theme while scrolling", async ({ page }) => { diff --git a/crates/diffcore-tauri/ui/tests/e2e/tauri-audit.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/tauri-audit.spec.ts index 6d3eaef3..8bef6edb 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/tauri-audit.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/tauri-audit.spec.ts @@ -203,6 +203,11 @@ test.describe("Error Boundaries", () => { await page.goto("/"); await waitForAnalysis(page); + // The Flow Graph only mounts under the Graph subtab in the right panel — + // open it so its error boundary can catch the crash. + await page.locator(".annotation-subtab", { hasText: "Graph" }).click(); + await expect(page.locator("[data-testid='flow-graph']")).toBeVisible(); + // Crash both Diff Viewer and Flow Graph await page.evaluate(() => { (window as any).__TEST_API__.crashPanel("Diff Viewer"); @@ -431,10 +436,11 @@ test.describe("Large Dataset Performance", () => { await expect(page.locator(".group-item").first()).toBeVisible({ timeout: 5_000 }); await page.waitForTimeout(500); - // Click the 50th group + // Click the 50th group's header — clicking a file row inside a group + // opens that file in a tab without switching the selected group. const targetGroup = page.locator(".group-item:not(.infra-group)").nth(49); const startTime = Date.now(); - await targetGroup.click(); + await targetGroup.locator(".group-name").click(); // Wait for it to become selected await expect(targetGroup).toHaveClass(/selected/, { timeout: 2_000 }); @@ -500,7 +506,8 @@ test.describe("State Desync Prevention", () => { const groupCount = await groups.count(); for (let i = 0; i < Math.min(groupCount, 3); i++) { - await groups.nth(i).click(); + // Click the group header — file rows open tabs without switching group + await groups.nth(i).locator(".group-name").click(); // Don't wait — rapid clicks } diff --git a/crates/diffcore-tauri/ui/tests/e2e/ui-improvements.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/ui-improvements.spec.ts index 7e985136..2f9e08a0 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/ui-improvements.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/ui-improvements.spec.ts @@ -2,10 +2,9 @@ * UI Improvements — Playwright E2E tests. * * Covers: - * - Comment strip below diff viewer - * - Click-to-scroll on comment in strip - * - Edges section collapsed by default - * - Edges section toggle + * - Comments tab in the right panel (comments moved out of the old strip below the diff) + * - Comment cards: text, type badge, delete, file grouping, persistence + * - Edges annotation subtab (replaced the old collapsible edges section) * - PNG/SVG export buttons removed * - MiniMap hidden for small graphs */ @@ -29,80 +28,88 @@ async function addCommentViaUI(page: Page, text: string) { await page.waitForTimeout(300); } -// ── Comment Strip Tests ── +async function openCommentsTab(page: Page) { + await page.getByTestId("comments-tab").click(); +} + +// ── Comments Tab Tests ── -test.describe("Comment Strip", () => { +test.describe("Comments Tab", () => { test.beforeEach(async ({ page }) => { await page.goto("/"); await waitForAnalysis(page); }); - test("01 — comment strip not visible when no comments exist", async ({ page }) => { - await expect(page.locator(".comment-strip")).not.toBeVisible(); + test("01 — comments tab shows empty state when no comments exist", async ({ page }) => { + await openCommentsTab(page); + await expect(page.locator(".comments-tab-empty")).toBeVisible(); }); - test("02 — comment strip appears after adding a file-level comment", async ({ page }) => { + test("02 — comment appears in comments tab after adding a file-level comment", async ({ page }) => { await addCommentViaUI(page, "Needs refactoring"); - const strip = page.locator(".comment-strip"); - await expect(strip).toBeVisible(); - - await expect(page.locator(".comment-strip-count")).toContainText("1"); + await openCommentsTab(page); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); }); - test("03 — comment strip shows multiple comments", async ({ page }) => { + test("03 — comments tab shows multiple comments", async ({ page }) => { await addCommentViaUI(page, "First comment"); await addCommentViaUI(page, "Second comment"); - await expect(page.locator(".comment-strip-count")).toContainText("2"); - const items = page.locator(".comment-strip-item"); - expect(await items.count()).toBe(2); + await expect(page.getByTestId("comments-tab").locator(".panel-tab-count")).toHaveText("2"); + + await openCommentsTab(page); + await expect(page.locator(".comments-tab-card")).toHaveCount(2); }); - test("04 — comment strip item shows comment text", async ({ page }) => { + test("04 — comment card shows comment text", async ({ page }) => { await addCommentViaUI(page, "Check error handling here"); - const item = page.locator(".comment-strip-item").first(); - await expect(item.locator(".comment-strip-text")).toContainText("Check error handling here"); + await openCommentsTab(page); + const card = page.locator(".comments-tab-card").first(); + await expect(card.locator(".comments-tab-card-text")).toContainText("Check error handling here"); }); - test("05 — comment strip item shows type badge", async ({ page }) => { + test("05 — comment card shows type badge", async ({ page }) => { await addCommentViaUI(page, "File level note"); - const badge = page.locator(".comment-strip-badge").first(); + await openCommentsTab(page); + const badge = page.locator(".comments-tab-card .comment-strip-badge").first(); await expect(badge).toBeVisible(); // Should be "file" type since file is selected await expect(badge).toContainText("file"); }); - test("06 — deleting comment from strip removes it", async ({ page }) => { + test("06 — deleting comment from comments tab removes it", async ({ page }) => { await addCommentViaUI(page, "Delete me"); - await expect(page.locator(".comment-strip")).toBeVisible(); + await openCommentsTab(page); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); // Click delete button await page.locator(".comment-strip-delete").first().click(); await page.waitForTimeout(300); - // Strip should disappear (no more comments) - await expect(page.locator(".comment-strip")).not.toBeVisible(); + // Card should be gone, empty state back + await expect(page.locator(".comments-tab-empty")).toBeVisible(); }); - test("07 — comment strip hides when switching to a file with no comments", async ({ page }) => { + test("07 — comments are grouped under their file path", async ({ page }) => { await addCommentViaUI(page, "Comment on first file"); - await expect(page.locator(".comment-strip")).toBeVisible(); - - // Navigate to next file - await page.keyboard.press("j"); - await page.waitForTimeout(500); - // Strip should not be visible for the new file (no comments there) - await expect(page.locator(".comment-strip")).not.toBeVisible(); + await openCommentsTab(page); + const header = page.locator(".comments-tab-file-header").first(); + await expect(header).toBeVisible(); + const pathText = await header.locator(".comments-tab-file-path").textContent(); + expect(pathText!.length).toBeGreaterThan(0); + await expect(header.locator(".comments-tab-file-count")).toHaveText("1"); }); - test("08 — comment strip reappears when navigating back to commented file", async ({ page }) => { + test("08 — comments persist when navigating between files", async ({ page }) => { await addCommentViaUI(page, "Persistent comment"); - await expect(page.locator(".comment-strip")).toBeVisible(); + + await openCommentsTab(page); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); // Navigate away and back await page.keyboard.press("j"); @@ -110,53 +117,52 @@ test.describe("Comment Strip", () => { await page.keyboard.press("k"); await page.waitForTimeout(500); - await expect(page.locator(".comment-strip")).toBeVisible(); - await expect(page.locator(".comment-strip-text")).toContainText("Persistent comment"); + await expect(page.locator(".comments-tab-card")).toHaveCount(1); + await expect(page.locator(".comments-tab-card-text")).toContainText("Persistent comment"); }); }); -// ── Edges Section Tests ── +// ── Edges Subtab Tests ── -test.describe("Edges Section", () => { +test.describe("Edges Subtab", () => { test.beforeEach(async ({ page }) => { await page.goto("/"); await waitForAnalysis(page); }); - test("09 — edges section is collapsed by default", async ({ page }) => { - await expect(page.locator(".edges-section")).toHaveClass(/edges-collapsed/); - const edgesToggle = page.locator(".edges-section .section-toggle"); - await expect(edgesToggle).toBeVisible(); - await expect(page.locator(".edges-section .section-toggle-label")).toContainText("Edges"); + test("09 — edge list is hidden by default (Info subtab active)", async ({ page }) => { + const infoTab = page.locator(".annotation-subtab", { hasText: "Info" }); + await expect(infoTab).toHaveClass(/active/); + await expect(page.locator(".annotation-subtab", { hasText: "Edges" })).toBeVisible(); + await expect(page.locator(".edges-section")).not.toBeVisible(); }); - test("10 — clicking edges toggle expands the edge list", async ({ page }) => { - const edgesToggle = page.locator(".edges-section .section-toggle"); - await edgesToggle.click(); + test("10 — clicking edges subtab shows the edge list", async ({ page }) => { + const edgesTab = page.locator(".annotation-subtab", { hasText: "Edges" }); + await edgesTab.click(); await page.waitForTimeout(300); - await expect(page.locator(".edges-section")).not.toHaveClass(/edges-collapsed/); await expect(page.locator(".edge-list")).toBeVisible(); const items = page.locator(".edge-item"); expect(await items.count()).toBeGreaterThan(0); }); - test("11 — clicking edges toggle again collapses it", async ({ page }) => { - const edgesToggle = page.locator(".edges-section .section-toggle"); + test("11 — switching back to Info subtab hides the edge list", async ({ page }) => { + const edgesTab = page.locator(".annotation-subtab", { hasText: "Edges" }); - // Expand - await edgesToggle.click(); + // Show edges + await edgesTab.click(); await page.waitForTimeout(300); await expect(page.locator(".edge-list")).toBeVisible(); - // Collapse - await edgesToggle.click(); + // Back to Info + await page.locator(".annotation-subtab", { hasText: "Info" }).click(); await page.waitForTimeout(300); - await expect(page.locator(".edges-section")).toHaveClass(/edges-collapsed/); + await expect(page.locator(".edges-section")).not.toBeVisible(); }); - test("12 — edges toggle shows edge count", async ({ page }) => { - const count = page.locator(".edges-section .section-toggle-count"); + test("12 — edges subtab shows edge count", async ({ page }) => { + const count = page.locator(".annotation-subtab", { hasText: "Edges" }).locator(".annotation-subtab-count"); await expect(count).toBeVisible(); expect(await count.textContent()).toMatch(/^\d+$/); }); @@ -169,6 +175,10 @@ test.describe("Export Buttons Removed", () => { await page.goto("/"); await waitForAnalysis(page); + // Mount the graph first so the absence assertions are non-vacuous + await page.locator(".annotation-subtab", { hasText: "Graph" }).click(); + await expect(page.locator("[data-testid='flow-graph']")).toBeVisible(); + await expect(page.locator(".flow-export-buttons")).not.toBeVisible(); await expect(page.locator(".flow-export-btn")).not.toBeVisible(); }); @@ -181,6 +191,10 @@ test.describe("MiniMap", () => { await page.goto("/"); await waitForAnalysis(page); + // Mount the graph first so the absence assertion is non-vacuous + await page.locator(".annotation-subtab", { hasText: "Graph" }).click(); + await expect(page.locator("[data-testid='flow-graph']")).toBeVisible(); + // The demo data has ~4-6 nodes per group, well under 15 await expect(page.locator(".react-flow__minimap")).not.toBeVisible(); }); diff --git a/crates/diffcore-tauri/ui/tests/e2e/visual-polish.spec.ts b/crates/diffcore-tauri/ui/tests/e2e/visual-polish.spec.ts index 4663bdf5..6d59d943 100644 --- a/crates/diffcore-tauri/ui/tests/e2e/visual-polish.spec.ts +++ b/crates/diffcore-tauri/ui/tests/e2e/visual-polish.spec.ts @@ -80,22 +80,25 @@ test.describe("Visual Polish — Screenshot Baseline", () => { await page.goto("/"); await waitForAnalysis(page); - await page.locator(".panel-right").screenshot({ - path: path.join(SCREENSHOTS_DIR, "04-annotations-panel.png"), - }); - - // Verify group details shown + // Verify group details shown (Info sub-tab, active by default) await expect(page.locator(".group-detail-name")).toContainText("POST /api/users"); await expect(page.locator(".entrypoint-info")).toBeVisible(); - // Verify React Flow graph rendered + // The flow graph now lives behind the "Graph" sub-tab + await page.locator(".annotation-subtab", { hasText: "Graph" }).click(); await expect(page.locator("[data-testid='flow-graph'] .react-flow")).toBeVisible(); // Verify flow nodes rendered (one per file in group) const flowNodes = page.locator(".flow-node"); + await expect(flowNodes.first()).toBeVisible(); expect(await flowNodes.count()).toBeGreaterThanOrEqual(3); - // Verify edges list + await page.locator(".panel-right").screenshot({ + path: path.join(SCREENSHOTS_DIR, "04-annotations-panel.png"), + }); + + // Verify edges list behind the "Edges" sub-tab + await page.locator(".annotation-subtab", { hasText: "Edges" }).click(); await expect(page.locator(".edge-list")).toBeVisible(); const edges = page.locator(".edge-item"); expect(await edges.count()).toBeGreaterThanOrEqual(3); @@ -123,9 +126,10 @@ test.describe("Visual Polish — Screenshot Baseline", () => { await page.goto("/"); await waitForAnalysis(page); - // Click on the third group + // Click on the third group's header (file lists are always expanded, so a + // click on the item's center would land on a file row and only open that file) const thirdGroup = page.locator(".group-item").nth(2); - await thirdGroup.click(); + await thirdGroup.locator(".group-name").click(); await page.waitForTimeout(1500); await page.screenshot({ @@ -234,22 +238,28 @@ test.describe("Visual Polish — Screenshot Baseline", () => { // Verify inputs populated await expect(page.locator(".repo-input")).toHaveValue("/demo/repo"); - // Base branch is now a dropdown; verify the displayed branch name - await expect(page.locator(".branch-dropdown-trigger .branch-name")).toContainText("main"); + // There are now two branch dropdowns (head + base); verify the base one shows main + await expect( + page.getByTestId("base-branch-dropdown").locator(".branch-name"), + ).toContainText("main"); }); test("11 — flow graph close-up", async ({ page }) => { await page.goto("/"); await waitForAnalysis(page); - await page.locator(".flow-graph-container").screenshot({ - path: path.join(SCREENSHOTS_DIR, "11-flow-graph.png"), - }); + // The flow graph now lives behind the "Graph" sub-tab in the right panel + await page.locator(".annotation-subtab", { hasText: "Graph" }).click(); // Verify React Flow rendered with nodes const flowNodes = page.locator(".flow-node"); + await expect(flowNodes.first()).toBeVisible(); expect(await flowNodes.count()).toBeGreaterThanOrEqual(3); + await page.locator(".flow-graph-container").screenshot({ + path: path.join(SCREENSHOTS_DIR, "11-flow-graph.png"), + }); + // Verify nodes have labels and roles await expect(flowNodes.first().locator(".flow-node-label")).toBeVisible(); await expect(flowNodes.first().locator(".flow-node-role")).toBeVisible(); diff --git a/docs/screenshots/01-loaded-analysis.png b/docs/screenshots/01-loaded-analysis.png index ddca0a58..6e5b334a 100644 Binary files a/docs/screenshots/01-loaded-analysis.png and b/docs/screenshots/01-loaded-analysis.png differ diff --git a/docs/screenshots/02-flow-groups-panel.png b/docs/screenshots/02-flow-groups-panel.png index 1337e558..8167297d 100644 Binary files a/docs/screenshots/02-flow-groups-panel.png and b/docs/screenshots/02-flow-groups-panel.png differ diff --git a/docs/screenshots/03-diff-viewer.png b/docs/screenshots/03-diff-viewer.png index 012a061a..4bb9bbe8 100644 Binary files a/docs/screenshots/03-diff-viewer.png and b/docs/screenshots/03-diff-viewer.png differ diff --git a/docs/screenshots/04-annotations-panel.png b/docs/screenshots/04-annotations-panel.png index efcaae86..b8bed55a 100644 Binary files a/docs/screenshots/04-annotations-panel.png and b/docs/screenshots/04-annotations-panel.png differ diff --git a/docs/screenshots/05-second-group-selected.png b/docs/screenshots/05-second-group-selected.png index 4c077b29..fdbac92f 100644 Binary files a/docs/screenshots/05-second-group-selected.png and b/docs/screenshots/05-second-group-selected.png differ diff --git a/docs/screenshots/06-third-group-low-risk.png b/docs/screenshots/06-third-group-low-risk.png index 7b772d21..78ec7175 100644 Binary files a/docs/screenshots/06-third-group-low-risk.png and b/docs/screenshots/06-third-group-low-risk.png differ diff --git a/docs/screenshots/07-second-file-selected.png b/docs/screenshots/07-second-file-selected.png index 314bdc21..8f9beb7b 100644 Binary files a/docs/screenshots/07-second-file-selected.png and b/docs/screenshots/07-second-file-selected.png differ diff --git a/docs/screenshots/08-keyboard-navigation.png b/docs/screenshots/08-keyboard-navigation.png index f78020d9..280ac165 100644 Binary files a/docs/screenshots/08-keyboard-navigation.png and b/docs/screenshots/08-keyboard-navigation.png differ diff --git a/docs/screenshots/09-group-keyboard-navigation.png b/docs/screenshots/09-group-keyboard-navigation.png index 110820b9..10f7e587 100644 Binary files a/docs/screenshots/09-group-keyboard-navigation.png and b/docs/screenshots/09-group-keyboard-navigation.png differ diff --git a/docs/screenshots/10-top-bar.png b/docs/screenshots/10-top-bar.png index a9b35b34..0ce9c039 100644 Binary files a/docs/screenshots/10-top-bar.png and b/docs/screenshots/10-top-bar.png differ diff --git a/docs/screenshots/11-flow-graph.png b/docs/screenshots/11-flow-graph.png index 737d81fb..982e4767 100644 Binary files a/docs/screenshots/11-flow-graph.png and b/docs/screenshots/11-flow-graph.png differ diff --git a/docs/screenshots/12-error-state.png b/docs/screenshots/12-error-state.png index bef67029..cec45827 100644 Binary files a/docs/screenshots/12-error-state.png and b/docs/screenshots/12-error-state.png differ diff --git a/docs/screenshots/13-infrastructure-group.png b/docs/screenshots/13-infrastructure-group.png index db76a6dc..fae9cc92 100644 Binary files a/docs/screenshots/13-infrastructure-group.png and b/docs/screenshots/13-infrastructure-group.png differ diff --git a/docs/screenshots/14-keyboard-hints.png b/docs/screenshots/14-keyboard-hints.png index 2e893895..f728d8d3 100644 Binary files a/docs/screenshots/14-keyboard-hints.png and b/docs/screenshots/14-keyboard-hints.png differ diff --git a/docs/screenshots/15-branch-dropdown-open.png b/docs/screenshots/15-branch-dropdown-open.png index f7efa0b4..e70cb1e6 100644 Binary files a/docs/screenshots/15-branch-dropdown-open.png and b/docs/screenshots/15-branch-dropdown-open.png differ diff --git a/docs/screenshots/16-branch-selected-highlight.png b/docs/screenshots/16-branch-selected-highlight.png index 0dd8774e..276eb4cf 100644 Binary files a/docs/screenshots/16-branch-selected-highlight.png and b/docs/screenshots/16-branch-selected-highlight.png differ diff --git a/docs/screenshots/17-branch-dropdown-many.png b/docs/screenshots/17-branch-dropdown-many.png index 484fb9f9..01a306db 100644 Binary files a/docs/screenshots/17-branch-dropdown-many.png and b/docs/screenshots/17-branch-dropdown-many.png differ diff --git a/docs/screenshots/18-push-status-ahead.png b/docs/screenshots/18-push-status-ahead.png index eaf1510f..0cf0651d 100644 Binary files a/docs/screenshots/18-push-status-ahead.png and b/docs/screenshots/18-push-status-ahead.png differ diff --git a/docs/screenshots/19-push-status-diverged.png b/docs/screenshots/19-push-status-diverged.png index f8c1bc7a..1e92bf26 100644 Binary files a/docs/screenshots/19-push-status-diverged.png and b/docs/screenshots/19-push-status-diverged.png differ diff --git a/docs/screenshots/20-push-status-uptodate.png b/docs/screenshots/20-push-status-uptodate.png index 67ee9f23..83311e4f 100644 Binary files a/docs/screenshots/20-push-status-uptodate.png and b/docs/screenshots/20-push-status-uptodate.png differ diff --git a/docs/screenshots/21-worktree-multiple.png b/docs/screenshots/21-worktree-multiple.png index 72c2e5c9..02c96ff9 100644 Binary files a/docs/screenshots/21-worktree-multiple.png and b/docs/screenshots/21-worktree-multiple.png differ diff --git a/docs/screenshots/22-settings-panel.png b/docs/screenshots/22-settings-panel.png index 609e9536..147c0267 100644 Binary files a/docs/screenshots/22-settings-panel.png and b/docs/screenshots/22-settings-panel.png differ diff --git a/docs/screenshots/23-api-key-configured.png b/docs/screenshots/23-api-key-configured.png index dd4ce57b..d8ea3e0b 100644 Binary files a/docs/screenshots/23-api-key-configured.png and b/docs/screenshots/23-api-key-configured.png differ diff --git a/docs/screenshots/24-api-key-missing.png b/docs/screenshots/24-api-key-missing.png index 978e4ba3..0896e7b0 100644 Binary files a/docs/screenshots/24-api-key-missing.png and b/docs/screenshots/24-api-key-missing.png differ diff --git a/docs/screenshots/25-refinement-settings-expanded.png b/docs/screenshots/25-refinement-settings-expanded.png index b0aa061d..5a516614 100644 Binary files a/docs/screenshots/25-refinement-settings-expanded.png and b/docs/screenshots/25-refinement-settings-expanded.png differ diff --git a/docs/screenshots/27-summarize-idle.png b/docs/screenshots/27-summarize-idle.png index 0f3a27e3..f7ab1824 100644 Binary files a/docs/screenshots/27-summarize-idle.png and b/docs/screenshots/27-summarize-idle.png differ diff --git a/docs/screenshots/28-summarize-complete.png b/docs/screenshots/28-summarize-complete.png index b7de7ec5..b3d1943e 100644 Binary files a/docs/screenshots/28-summarize-complete.png and b/docs/screenshots/28-summarize-complete.png differ diff --git a/docs/screenshots/29-deep-analysis-complete.png b/docs/screenshots/29-deep-analysis-complete.png index 4dce1f2b..1e5ae70e 100644 Binary files a/docs/screenshots/29-deep-analysis-complete.png and b/docs/screenshots/29-deep-analysis-complete.png differ diff --git a/docs/screenshots/30-buttons-no-api-key.png b/docs/screenshots/30-buttons-no-api-key.png index e9cdfef0..0d282983 100644 Binary files a/docs/screenshots/30-buttons-no-api-key.png and b/docs/screenshots/30-buttons-no-api-key.png differ diff --git a/docs/screenshots/31-refinement-banner.png b/docs/screenshots/31-refinement-banner.png index 6f4fcec2..fe96ff21 100644 Binary files a/docs/screenshots/31-refinement-banner.png and b/docs/screenshots/31-refinement-banner.png differ diff --git a/docs/screenshots/32-refinement-complete.png b/docs/screenshots/32-refinement-complete.png index dff5b5d3..4d143df5 100644 Binary files a/docs/screenshots/32-refinement-complete.png and b/docs/screenshots/32-refinement-complete.png differ diff --git a/docs/screenshots/33-refinement-change-indicators.png b/docs/screenshots/33-refinement-change-indicators.png index e6488365..e285a97a 100644 Binary files a/docs/screenshots/33-refinement-change-indicators.png and b/docs/screenshots/33-refinement-change-indicators.png differ diff --git a/docs/screenshots/34-refinement-original-view.png b/docs/screenshots/34-refinement-original-view.png index 656ecd91..d7accec9 100644 Binary files a/docs/screenshots/34-refinement-original-view.png and b/docs/screenshots/34-refinement-original-view.png differ diff --git a/docs/screenshots/35-graph-node-selected.png b/docs/screenshots/35-graph-node-selected.png index 85236780..262b91ee 100644 Binary files a/docs/screenshots/35-graph-node-selected.png and b/docs/screenshots/35-graph-node-selected.png differ diff --git a/docs/screenshots/36-graph-legend-expanded.png b/docs/screenshots/36-graph-legend-expanded.png index 4650a5b8..2d2cc385 100644 Binary files a/docs/screenshots/36-graph-legend-expanded.png and b/docs/screenshots/36-graph-legend-expanded.png differ diff --git a/docs/screenshots/37-graph-fullscreen.png b/docs/screenshots/37-graph-fullscreen.png index 1ee072d2..ccf3c7f0 100644 Binary files a/docs/screenshots/37-graph-fullscreen.png and b/docs/screenshots/37-graph-fullscreen.png differ diff --git a/docs/screenshots/39-single-node-no-graph.png b/docs/screenshots/39-single-node-no-graph.png index d6ff0436..5ad4cccf 100644 Binary files a/docs/screenshots/39-single-node-no-graph.png and b/docs/screenshots/39-single-node-no-graph.png differ diff --git a/docs/screenshots/40-empty-state.png b/docs/screenshots/40-empty-state.png index cc395116..b8bc66ed 100644 Binary files a/docs/screenshots/40-empty-state.png and b/docs/screenshots/40-empty-state.png differ diff --git a/docs/screenshots/41-error-state-real.png b/docs/screenshots/41-error-state-real.png index 88a46d6f..8ae22f96 100644 Binary files a/docs/screenshots/41-error-state-real.png and b/docs/screenshots/41-error-state-real.png differ diff --git a/docs/screenshots/42-right-panel-empty.png b/docs/screenshots/42-right-panel-empty.png index 4bd9067f..44066ae2 100644 Binary files a/docs/screenshots/42-right-panel-empty.png and b/docs/screenshots/42-right-panel-empty.png differ diff --git a/docs/screenshots/43-large-dataset.png b/docs/screenshots/43-large-dataset.png index a0490de4..2292f073 100644 Binary files a/docs/screenshots/43-large-dataset.png and b/docs/screenshots/43-large-dataset.png differ diff --git a/docs/screenshots/44-large-dataset-scrolled.png b/docs/screenshots/44-large-dataset-scrolled.png index 9a328cc6..c20edffe 100644 Binary files a/docs/screenshots/44-large-dataset-scrolled.png and b/docs/screenshots/44-large-dataset-scrolled.png differ diff --git a/docs/screenshots/45-large-infra-group.png b/docs/screenshots/45-large-infra-group.png index 37778b5b..df6bc6f2 100644 Binary files a/docs/screenshots/45-large-infra-group.png and b/docs/screenshots/45-large-infra-group.png differ diff --git a/docs/screenshots/46-responsive-narrow.png b/docs/screenshots/46-responsive-narrow.png index 5b1ba6ce..b90bc06a 100644 Binary files a/docs/screenshots/46-responsive-narrow.png and b/docs/screenshots/46-responsive-narrow.png differ diff --git a/docs/screenshots/47-responsive-wide.png b/docs/screenshots/47-responsive-wide.png index 56dd3a38..49311d4f 100644 Binary files a/docs/screenshots/47-responsive-wide.png and b/docs/screenshots/47-responsive-wide.png differ diff --git a/docs/screenshots/48-responsive-minimum.png b/docs/screenshots/48-responsive-minimum.png index 545b0e84..8bfc5be6 100644 Binary files a/docs/screenshots/48-responsive-minimum.png and b/docs/screenshots/48-responsive-minimum.png differ diff --git a/docs/screenshots/49-pr-preview-default.png b/docs/screenshots/49-pr-preview-default.png index 0bcbcc34..0ce9c039 100644 Binary files a/docs/screenshots/49-pr-preview-default.png and b/docs/screenshots/49-pr-preview-default.png differ diff --git a/docs/screenshots/50-pr-preview-switched-branch.png b/docs/screenshots/50-pr-preview-switched-branch.png index 4c27a6c7..a5436823 100644 Binary files a/docs/screenshots/50-pr-preview-switched-branch.png and b/docs/screenshots/50-pr-preview-switched-branch.png differ diff --git a/docs/screenshots/51-replay-active.png b/docs/screenshots/51-replay-active.png index 2f659f23..3f4fd27a 100644 Binary files a/docs/screenshots/51-replay-active.png and b/docs/screenshots/51-replay-active.png differ diff --git a/docs/screenshots/52-replay-step-2.png b/docs/screenshots/52-replay-step-2.png index 94d5fbbe..1158acef 100644 Binary files a/docs/screenshots/52-replay-step-2.png and b/docs/screenshots/52-replay-step-2.png differ diff --git a/docs/screenshots/53-replay-visited-checks.png b/docs/screenshots/53-replay-visited-checks.png index d3eaddce..1158acef 100644 Binary files a/docs/screenshots/53-replay-visited-checks.png and b/docs/screenshots/53-replay-visited-checks.png differ diff --git a/docs/screenshots/54-replay-last-step.png b/docs/screenshots/54-replay-last-step.png index c0773ed6..4aab2cf6 100644 Binary files a/docs/screenshots/54-replay-last-step.png and b/docs/screenshots/54-replay-last-step.png differ diff --git a/docs/screenshots/60-analysis-loaded.png b/docs/screenshots/60-analysis-loaded.png index 256a7e77..6e5b334a 100644 Binary files a/docs/screenshots/60-analysis-loaded.png and b/docs/screenshots/60-analysis-loaded.png differ diff --git a/docs/screenshots/61-comment-strip.png b/docs/screenshots/61-comment-strip.png deleted file mode 100644 index 254f5a41..00000000 Binary files a/docs/screenshots/61-comment-strip.png and /dev/null differ diff --git a/docs/screenshots/62-comment-strip-closeup.png b/docs/screenshots/62-comment-strip-closeup.png deleted file mode 100644 index 68aa51b0..00000000 Binary files a/docs/screenshots/62-comment-strip-closeup.png and /dev/null differ diff --git a/docs/screenshots/62-comments-tab-closeup.png b/docs/screenshots/62-comments-tab-closeup.png new file mode 100644 index 00000000..c11dea44 Binary files /dev/null and b/docs/screenshots/62-comments-tab-closeup.png differ diff --git a/docs/screenshots/65-flow-graph.png b/docs/screenshots/65-flow-graph.png deleted file mode 100644 index 4644c25f..00000000 Binary files a/docs/screenshots/65-flow-graph.png and /dev/null differ diff --git a/docs/screenshots/66-flow-graph-fullscreen.png b/docs/screenshots/66-flow-graph-fullscreen.png deleted file mode 100644 index df64b0af..00000000 Binary files a/docs/screenshots/66-flow-graph-fullscreen.png and /dev/null differ diff --git a/docs/screenshots/67-open-with-dropdown.png b/docs/screenshots/67-open-with-dropdown.png index 4753671f..18997a58 100644 Binary files a/docs/screenshots/67-open-with-dropdown.png and b/docs/screenshots/67-open-with-dropdown.png differ diff --git a/docs/screenshots/68-keyboard-hints.png b/docs/screenshots/68-keyboard-hints.png index 2e893895..f728d8d3 100644 Binary files a/docs/screenshots/68-keyboard-hints.png and b/docs/screenshots/68-keyboard-hints.png differ diff --git a/docs/screenshots/69-flow-groups-panel.png b/docs/screenshots/69-flow-groups-panel.png index 1337e558..8167297d 100644 Binary files a/docs/screenshots/69-flow-groups-panel.png and b/docs/screenshots/69-flow-groups-panel.png differ diff --git a/docs/screenshots/70-second-group.png b/docs/screenshots/70-second-group.png index 03a53ecb..4f2e15a8 100644 Binary files a/docs/screenshots/70-second-group.png and b/docs/screenshots/70-second-group.png differ diff --git a/docs/screenshots/71-replay-mode.png b/docs/screenshots/71-replay-mode.png index 5d59cd82..3f4fd27a 100644 Binary files a/docs/screenshots/71-replay-mode.png and b/docs/screenshots/71-replay-mode.png differ diff --git a/docs/screenshots/72-annotations-panel.png b/docs/screenshots/72-annotations-panel.png index 1881b3ba..2afd9669 100644 Binary files a/docs/screenshots/72-annotations-panel.png and b/docs/screenshots/72-annotations-panel.png differ diff --git a/docs/screenshots/80-editor-no-comments.png b/docs/screenshots/80-editor-no-comments.png index d827ddb2..6e5b334a 100644 Binary files a/docs/screenshots/80-editor-no-comments.png and b/docs/screenshots/80-editor-no-comments.png differ diff --git a/docs/screenshots/84-monaco-glyph-check.png b/docs/screenshots/84-monaco-glyph-check.png index 012a061a..4bb9bbe8 100644 Binary files a/docs/screenshots/84-monaco-glyph-check.png and b/docs/screenshots/84-monaco-glyph-check.png differ diff --git a/docs/screenshots/85-glyph-after-comment.png b/docs/screenshots/85-glyph-after-comment.png index a72fe57b..2fd225f1 100644 Binary files a/docs/screenshots/85-glyph-after-comment.png and b/docs/screenshots/85-glyph-after-comment.png differ diff --git a/docs/screenshots/86-glyph-hover.png b/docs/screenshots/86-glyph-hover.png index 1db6bed6..1d011a33 100644 Binary files a/docs/screenshots/86-glyph-hover.png and b/docs/screenshots/86-glyph-hover.png differ diff --git a/docs/screenshots/87-glyph-click-activates-comment.png b/docs/screenshots/87-glyph-click-activates-comment.png index 5fd5b64a..4fd73169 100644 Binary files a/docs/screenshots/87-glyph-click-activates-comment.png and b/docs/screenshots/87-glyph-click-activates-comment.png differ diff --git a/docs/screenshots/annotations-panel.png b/docs/screenshots/annotations-panel.png index baa1359f..2afd9669 100644 Binary files a/docs/screenshots/annotations-panel.png and b/docs/screenshots/annotations-panel.png differ diff --git a/docs/screenshots/comments-gutter.png b/docs/screenshots/comments-gutter.png index 306865a5..d7d06a33 100644 Binary files a/docs/screenshots/comments-gutter.png and b/docs/screenshots/comments-gutter.png differ diff --git a/docs/screenshots/hero-analysis.png b/docs/screenshots/hero-analysis.png index 6232f1a9..6e5b334a 100644 Binary files a/docs/screenshots/hero-analysis.png and b/docs/screenshots/hero-analysis.png differ diff --git a/docs/screenshots/keyboard-hints.png b/docs/screenshots/keyboard-hints.png index 2e893895..f728d8d3 100644 Binary files a/docs/screenshots/keyboard-hints.png and b/docs/screenshots/keyboard-hints.png differ diff --git a/docs/screenshots/open-with.png b/docs/screenshots/open-with.png index 4753671f..18997a58 100644 Binary files a/docs/screenshots/open-with.png and b/docs/screenshots/open-with.png differ diff --git a/docs/screenshots/replay-mode.png b/docs/screenshots/replay-mode.png index d2a8d005..3f4fd27a 100644 Binary files a/docs/screenshots/replay-mode.png and b/docs/screenshots/replay-mode.png differ diff --git a/docs/screenshots/second-group.png b/docs/screenshots/second-group.png index 26e95ca9..ad5d745b 100644 Binary files a/docs/screenshots/second-group.png and b/docs/screenshots/second-group.png differ