From da3faa77912001707df8e6a1b6405881c37bd629 Mon Sep 17 00:00:00 2001 From: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Date: Sat, 12 Sep 2026 21:58:33 +0000 Subject: [PATCH 1/2] fix(web): move viewer controls outside media and restore arrow navigation --- .../components/chat/ExpandedImageDialog.tsx | 75 +++++++++++-------- .../web/src/components/chat/ZoomableImage.tsx | 16 +++- 2 files changed, 54 insertions(+), 37 deletions(-) diff --git a/apps/web/src/components/chat/ExpandedImageDialog.tsx b/apps/web/src/components/chat/ExpandedImageDialog.tsx index f98bbe3982ba..641e3fe82cb1 100644 --- a/apps/web/src/components/chat/ExpandedImageDialog.tsx +++ b/apps/web/src/components/chat/ExpandedImageDialog.tsx @@ -1,4 +1,12 @@ -import { memo, useCallback, useEffect, useRef, useState, type ReactNode } from "react"; +import { + memo, + useCallback, + useEffect, + useRef, + useState, + type ReactNode, + type KeyboardEvent, +} from "react"; import { ChevronLeftIcon, ChevronRightIcon, ImageIcon, TextIcon, XIcon } from "lucide-react"; import { Button } from "../ui/button"; import { Dialog, DialogPopup, DialogTitle } from "../ui/dialog"; @@ -24,7 +32,7 @@ interface ExpandedImageDialogProps { } const EXPANDED_MEDIA_STATE_CLASS_NAME = - "flex aspect-auto h-48 min-h-0 w-[min(92vw,32rem)] flex-col items-center justify-center gap-3 rounded-lg border border-border/70 bg-black p-6 text-center text-sm text-white shadow-2xl"; + "flex aspect-auto h-48 min-h-0 w-[min(var(--media-width),32rem)] flex-col items-center justify-center gap-3 rounded-lg border border-border/70 bg-black p-6 text-center text-sm text-white shadow-2xl"; function ExpandedMediaFailure({ children }: { children: ReactNode }) { return ( @@ -51,8 +59,8 @@ function ExpandedVideo({ item }: { readonly item: ExpandedImageItem }) { originalUrl={item.originalUrl} preload="metadata" autoPlay={item.autoPlay ?? true} - className="block max-h-[86vh] max-w-[92vw] text-center" - videoClassName="aspect-auto max-h-[86vh] w-auto max-w-[92vw] rounded-lg border border-border/70 shadow-2xl" + className="block max-h-[var(--media-height)] max-w-[var(--media-width)] text-center" + videoClassName="aspect-auto max-h-[var(--media-height)] w-auto max-w-[var(--media-width)] rounded-lg border border-border/70 shadow-2xl" stateClassName={EXPANDED_MEDIA_STATE_CLASS_NAME} onRetry={asset ? refreshAssetUrl : undefined} /> @@ -111,29 +119,26 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ }; }, []); - useEffect(() => { - const onKeyDown = (event: globalThis.KeyboardEvent) => { - if (event.defaultPrevented || isContextMenuOpen()) return; - if (zoomableImageRef.current?.pan(event.key)) { - event.preventDefault(); - event.stopPropagation(); - return; - } - if (preview.images.length <= 1) return; - if (event.key === "ArrowLeft") { - event.preventDefault(); - event.stopPropagation(); - navigateImage(-1); - return; - } - if (event.key !== "ArrowRight") return; + const onKeyDown = (event: KeyboardEvent) => { + if (event.defaultPrevented || isContextMenuOpen() || event.target instanceof HTMLVideoElement) + return; + if (zoomableImageRef.current?.pan(event.key)) { event.preventDefault(); event.stopPropagation(); - navigateImage(1); - }; - window.addEventListener("keydown", onKeyDown); - return () => window.removeEventListener("keydown", onKeyDown); - }, [navigateImage, preview.images.length]); + return; + } + if (preview.images.length <= 1) return; + if (event.key === "ArrowLeft") { + event.preventDefault(); + event.stopPropagation(); + navigateImage(-1); + return; + } + if (event.key !== "ArrowRight") return; + event.preventDefault(); + event.stopPropagation(); + navigateImage(1); + }; useEffect(() => { const onEscape = (event: globalThis.KeyboardEvent) => { @@ -176,9 +181,13 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ bottomStickOnMobile={false} backdropClassName="z-[60]" viewportClassName="z-[60] grid-rows-1 place-items-center px-4 py-6 [-webkit-app-region:no-drag]" - className="row-start-1 max-h-[92vh] w-auto max-w-[92vw] overflow-visible" + className="row-start-1 max-h-[92vh] w-[92vw] max-w-[92vw] items-center overflow-visible [--media-width:92vw] [--media-height:min(86vh,calc(100vh-160px))] sm:[--media-width:calc(92vw-6rem)]" + onKeyDown={onKeyDown} initialFocus={closeButtonRef} finalFocus={() => returnFocusTarget} + onClick={(event) => { + if (event.target === event.currentTarget) onClose(); + }} > Expanded {mediaLabel} preview {preview.images.length > 1 && ( @@ -186,7 +195,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ type="button" size="icon" variant="media-navigation" - className="left-2 sm:left-6" + className="left-0 top-auto -bottom-12 translate-y-0 rounded-full bg-white/10 sm:top-1/2 sm:bottom-auto sm:-translate-y-1/2" aria-label="Previous media" onClick={() => navigateImage(-1)} > @@ -194,13 +203,13 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ )} -
+