50ac0f0daf
- Every Copy goes through copyText: on http://<lan-ip> navigator.clipboard is undefined, so the push token (shown once), snapshot links, tool paths and hosts copied nothing, silently or with a TypeError. - Inputs that submit on Enter use isSubmitEnter, so committing an IME composition (Vietnamese) no longer saves a half-typed pet name, jumps the settings search, or submits a cut-off plugin/MCP/marketplace spec. - LaneChangesPanel, Workspace features/proof and AiToolConfig drop a reply that lands after the next request started: opening file B before file A's read returned showed (and let the editor save) A's text under B's header. - Error-state Retry buttons spin through useSpin; ImageLightbox uses useDialog. - react-router-dom 6.30 -> 7.18.4 (GHSA-wrjc-x8rr-h8h6, GHSA-337j-9hxr-rhxg); screen snapshots gain only Link's data-discover attribute. - StatusBar test follows v2.8.17's "Settings → AI tools → Profiles" text.
47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
/**
|
|
* @file ImageLightbox.tsx
|
|
* @description One image shown full size over the whole page; a click or
|
|
* Escape closes it. Rendered into `document.body`, so no pane, its stacking
|
|
* layer or a header drawn over a pane can cover it — opened from inside a
|
|
* pane's Chat view, a `fixed` overlay would otherwise stay in that pane's
|
|
* layer, under anything stacked above it. `useDialog` takes the focus while it
|
|
* is open and gives it back, and its Escape closes only this, the top dialog.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { useRef } from "react";
|
|
import { createPortal } from "react-dom";
|
|
import { useDialog } from "../hooks/useDialog";
|
|
|
|
export function ImageLightbox({
|
|
src,
|
|
alt,
|
|
onClose,
|
|
}: {
|
|
src: string;
|
|
alt: string;
|
|
onClose: () => void;
|
|
}) {
|
|
const panel = useRef<HTMLDivElement>(null);
|
|
useDialog(true, panel, onClose);
|
|
return createPortal(
|
|
<div
|
|
ref={panel}
|
|
tabIndex={-1}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={alt}
|
|
data-testid="image-lightbox"
|
|
onClick={onClose}
|
|
className="fixed inset-0 z-[200] flex cursor-zoom-out items-center justify-center bg-black/60 p-8 outline-none"
|
|
>
|
|
<img
|
|
src={src}
|
|
alt={alt}
|
|
className="max-h-full max-w-full rounded-lg object-contain shadow-2xl"
|
|
/>
|
|
</div>,
|
|
document.body
|
|
);
|
|
}
|