From e127ed2d6cd2b51cfc256fc6e7e808aedd3fcfc0 Mon Sep 17 00:00:00 2001 From: Turtle Date: Mon, 5 Oct 2026 23:55:51 +0800 Subject: [PATCH] refactor(client): position the lightbox in a stage below the Windows caption --- apps/web/tests/markdown-images.e2e.ts | 2 +- .../src/ImageLightbox.module.css | 24 ++++++++++++------- .../ui-primitives/src/ImageLightbox.tsx | 12 ++++++---- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/apps/web/tests/markdown-images.e2e.ts b/apps/web/tests/markdown-images.e2e.ts index 4031ed8562..390675a3d1 100644 --- a/apps/web/tests/markdown-images.e2e.ts +++ b/apps/web/tests/markdown-images.e2e.ts @@ -289,7 +289,7 @@ describe('web e2e: Markdown image rendering', () => { await page.getByRole('button', { name: `View full image: ${WORKSPACE_ALT}`, exact: true }).click() const lightbox = page.getByRole('dialog', { name: 'Image preview', exact: true }) await lightbox.waitFor() - const lightboxMask = lightbox.locator(':scope > [aria-hidden="true"]') + const lightboxMask = lightbox.locator(':scope > div > [aria-hidden="true"]') const lightboxClose = lightbox.getByRole('button', { name: 'Close image preview' }) for (const windows of [true, false]) { await page.evaluate((value) => { diff --git a/packages/client/ui-primitives/src/ImageLightbox.module.css b/packages/client/ui-primitives/src/ImageLightbox.module.css index d24769cefd..4cffa16c36 100644 --- a/packages/client/ui-primitives/src/ImageLightbox.module.css +++ b/packages/client/ui-primitives/src/ImageLightbox.module.css @@ -1,16 +1,24 @@ +/* Full-viewport layer that blocks background clicks, including on the Windows caption. */ .backdrop { position: fixed; inset: 0; z-index: 1000; - display: grid; - place-items: center; - padding: calc(40px + var(--dsh-frame-chrome-top, 0px)) 40px 40px; } -/* Shared dimming mask below the preview and its controls; like Modal, it leaves the Windows caption unpainted. */ -.mask { +/* Painted area below the Windows caption; like Modal, the caption stays unpainted. */ +.stage { position: absolute; inset: var(--dsh-frame-chrome-top, 0px) 0 0; + display: flex; + align-items: center; + justify-content: center; + padding: 40px; +} + +/* Shared dimming mask below the preview and its controls. */ +.mask { + position: absolute; + inset: 0; background: var(--dsw-alias-bg-mask-1); backdrop-filter: var(--dsw-mask-blur); } @@ -18,7 +26,7 @@ .image { position: relative; max-width: min(100%, 1600px); - max-height: calc(100vh - 80px - var(--dsh-frame-chrome-top, 0px)); + max-height: 100%; object-fit: contain; border-radius: var(--dsw-radius-lg); background: var(--dsw-specific-input-major); @@ -27,8 +35,8 @@ } .close { - position: fixed; - top: calc(20px + var(--dsh-frame-chrome-top, 0px)); + position: absolute; + top: 20px; right: 20px; z-index: 1; display: grid; diff --git a/packages/client/ui-primitives/src/ImageLightbox.tsx b/packages/client/ui-primitives/src/ImageLightbox.tsx index 893ed2fe95..1cb3fe4a8c 100644 --- a/packages/client/ui-primitives/src/ImageLightbox.tsx +++ b/packages/client/ui-primitives/src/ImageLightbox.tsx @@ -54,11 +54,13 @@ export function ImageLightbox({ src, alt, labels, onClose }: { aria-modal="true" aria-label={labels.dialog} > -