From d8aaa0ab01dab2e37a8fb2eb2cb62573a80d6ec2 Mon Sep 17 00:00:00 2001 From: Maggie Appleton <5599295+MaggieAppleton@users.noreply.github.com> Date: Mon, 12 Oct 2026 10:40:17 +0100 Subject: [PATCH 1/2] Size the orphaned comments popover against the document, not its pill The orphaned comments card shares `.plan-comment-card`, whose width and max-height are percentages of the containing block. Its containing block was the pill's own absolutely positioned wrapper, so the card resolved to roughly the pill's width and less than its height, showing only a sliver of the first avatar. The wrapper now spans the layer's height, pointer-transparent, and the card takes its width from the document's inline-size container. Co-Authored-By: Claude Opus 5.5 --- e2e/sidecar.e2e.ts | 29 +++++++++++++++++++++++++++++ packages/editor/src/styles.css | 5 ++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/e2e/sidecar.e2e.ts b/e2e/sidecar.e2e.ts index ee839d2a0..8c6f496cc 100644 --- a/e2e/sidecar.e2e.ts +++ b/e2e/sidecar.e2e.ts @@ -1325,6 +1325,35 @@ test("a removed subject block moves its comment to document orphan chrome", asyn await expect(page.getByRole("dialog", { name: "Orphaned comments" })).toContainText(QUOTED); }); +test("the orphaned comments popover shows its whole card beneath the pill", async ({ join, seed }) => { + await seed(TWO_BLOCKS); + let page = await join("ana"); + await page.setViewportSize({ width: 1_280, height: 800 }); + let first = content(page).locator("p").first(); + await first.selectText(); + await page.keyboard.press("Backspace"); + await page.keyboard.press("Backspace"); + + let opener = page.getByRole("button", { name: "1 orphaned comments" }); + await opener.click(); + let popover = page.getByRole("dialog", { name: "Orphaned comments" }); + // The card sizes against the document, not the pill that holds it. + let reply = popover.getByRole("textbox", { name: "Reply", exact: true }); + await expect(reply).toBeInViewport({ ratio: 1 }); + await expect(popover.getByText(QUOTED, { exact: true })).toBeInViewport({ ratio: 1 }); + await expect.poll(() => popover.evaluate(element => element.scrollHeight - element.clientHeight)) + .toBeLessThanOrEqual(1); + let card = await popover.boundingBox(); + let pill = await opener.boundingBox(); + expect(card!.width).toBeGreaterThanOrEqual(280); + expect(card!.y).toBeGreaterThanOrEqual(pill!.y + pill!.height); + expect(Math.abs(card!.x + card!.width - (pill!.x + pill!.width))).toBeLessThanOrEqual(1); + + await page.keyboard.press("Escape"); + await expect(popover).toHaveCount(0); + await expect(opener).toBeFocused(); +}); + test("a compact orphan sheet owns focus and restores its opener", async ({ join, seed }) => { await seed(TWO_BLOCKS); let page = await join("ana", { diff --git a/packages/editor/src/styles.css b/packages/editor/src/styles.css index 2b071a589..1c9a422f9 100644 --- a/packages/editor/src/styles.css +++ b/packages/editor/src/styles.css @@ -1474,11 +1474,12 @@ transition-duration: var(--drawer-close-dur); } +/* Full height so the card's percentages resolve against the document, not the pill. */ .plan-comment-orphans { - pointer-events: auto; position: absolute; top: 0.5rem; right: 0.5rem; + bottom: 0.5rem; } .plan-comment-orphan-button { @@ -1490,6 +1491,8 @@ .plan-comment-orphan-card { top: 2.5rem; right: 0; + width: min(20rem, calc(100cqi - 1rem)); + max-height: calc(100% - 2.5rem); } /* The scrim every phone sheet shares, lighter while this sheet rests at its medium detent. */ From f0000c1aa7b65bb30d142420316c49d4af0411bc Mon Sep 17 00:00:00 2001 From: Maggie Appleton <5599295+MaggieAppleton@users.noreply.github.com> Date: Mon, 12 Oct 2026 11:01:49 +0100 Subject: [PATCH 2/2] Hide the formatting toolbar while a comment card is open A selection's fixed formatting toolbar stayed above an open orphan or anchored comment card and covered it. The orphan card's offset is now one custom property shared by its top and max-height. Co-Authored-By: Claude Opus 5.5 --- e2e/sidecar.e2e.ts | 20 ++++++++++++++++++++ packages/editor/src/styles.css | 10 ++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/e2e/sidecar.e2e.ts b/e2e/sidecar.e2e.ts index 8c6f496cc..a3f9af4c4 100644 --- a/e2e/sidecar.e2e.ts +++ b/e2e/sidecar.e2e.ts @@ -1325,6 +1325,19 @@ test("a removed subject block moves its comment to document orphan chrome", asyn await expect(page.getByRole("dialog", { name: "Orphaned comments" })).toContainText(QUOTED); }); +test("an open comment card hides the formatting toolbar of a lingering selection", async ({ join, seed }) => { + await seed(TWO_BLOCKS); + let page = await join("ana"); + await page.setViewportSize({ width: 1_280, height: 800 }); + await content(page).locator("p").nth(1).selectText(); + let toolbar = page.getByRole("toolbar", { name: "Text formatting" }); + await expect(toolbar).toBeVisible(); + + let card = await thread(page); + await expect(card).toBeVisible(); + await expect(toolbar).toBeHidden(); +}); + test("the orphaned comments popover shows its whole card beneath the pill", async ({ join, seed }) => { await seed(TWO_BLOCKS); let page = await join("ana"); @@ -1334,9 +1347,16 @@ test("the orphaned comments popover shows its whole card beneath the pill", asyn await page.keyboard.press("Backspace"); await page.keyboard.press("Backspace"); + // A selection's formatting toolbar would sit over the card it opens. + await content(page).locator("p").first().selectText(); + let toolbar = page.getByRole("toolbar", { name: "Text formatting" }); + await expect(toolbar).toBeVisible(); + let opener = page.getByRole("button", { name: "1 orphaned comments" }); await opener.click(); let popover = page.getByRole("dialog", { name: "Orphaned comments" }); + await expect(popover).toBeVisible(); + await expect(toolbar).toBeHidden(); // The card sizes against the document, not the pill that holds it. let reply = popover.getByRole("textbox", { name: "Reply", exact: true }); await expect(reply).toBeInViewport({ ratio: 1 }); diff --git a/packages/editor/src/styles.css b/packages/editor/src/styles.css index 1c9a422f9..df47825da 100644 --- a/packages/editor/src/styles.css +++ b/packages/editor/src/styles.css @@ -1489,10 +1489,11 @@ } .plan-comment-orphan-card { - top: 2.5rem; + --orphan-card-offset: 2.5rem; + top: var(--orphan-card-offset); right: 0; width: min(20rem, calc(100cqi - 1rem)); - max-height: calc(100% - 2.5rem); + max-height: calc(100% - var(--orphan-card-offset)); } /* The scrim every phone sheet shares, lighter while this sheet rests at its medium detent. */ @@ -5441,6 +5442,11 @@ body:has(.plan-comment-sheet-popup[data-swiping]) animation: plan-bubble-enter var(--duration-fast) var(--ease-out); } +/* An open comment card owns the reader's attention; a lingering selection's toolbar would cover it. */ +:root:has(.plan-comment-card.motion-comment-surface:not(.is-closing)) .plan-formatting-toolbar { + display: none; +} + @media (prefers-reduced-motion: reduce) { .plan-formatting-toolbar { animation: none;