authorgravatar for 113233555+caezium@users.noreply.github.comHenry Zhang <113233555+caezium@users.noreply.github.com> 2026-09-11 02:37:15+08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-10 15:37:15-03:00
log3997b9a3a4ee6b43ee768a9b372ff3dd52fd840e
tree1a84b4ab478819cb9690d88d8f1faef006273e41
parent21d744039bf76c072c875886ee497e391a0dd430
signature Signed by PGP key B5690EEEBB952194

fix(web): align floating browser preview corners (#10915)


1 files changed, 17 insertions(+), 5 deletions(-)

apps/web/src/components/preview/ThreadPreviewMiniPlayer.tsx+17-5
...@@ -44,6 +44,8 @@ interface Props {...@@ -44,6 +44,8 @@ interface Props {
44 readonly bottomInset: number;44 readonly bottomInset: number;
45}45}
4646
47const PREVIEW_MINI_PLAYER_CORNER_RADIUS = 12;
48
47// Invisible grab zones straddling each edge; the cursor is the only affordance.49// Invisible grab zones straddling each edge; the cursor is the only affordance.
48const RESIZE_HANDLES: ReadonlyArray<{50const RESIZE_HANDLES: ReadonlyArray<{
49 readonly direction: BrowserViewportResizeDirection;51 readonly direction: BrowserViewportResizeDirection;
...@@ -59,6 +61,10 @@ const RESIZE_HANDLES: ReadonlyArray<{...@@ -59,6 +61,10 @@ const RESIZE_HANDLES: ReadonlyArray<{
59 { direction: "southeast", className: "-bottom-2 -right-2 size-4 cursor-nwse-resize" },61 { direction: "southeast", className: "-bottom-2 -right-2 size-4 cursor-nwse-resize" },
60];62];
6163
64/**
65 * Floats the thread's browser surface over chat. Native clipping and the DOM
66 * frame use the same radius so their separately composited edges stay aligned.
67 */
62export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props) {68export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props) {
63 const containerRef = useRef<HTMLDivElement | null>(null);69 const containerRef = useRef<HTMLDivElement | null>(null);
64 const gestureRef = useRef<PointerGesture | null>(null);70 const gestureRef = useRef<PointerGesture | null>(null);
...@@ -193,7 +199,13 @@ export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props...@@ -193,7 +199,13 @@ export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props
193 aria-label="Floating browser preview"199 aria-label="Floating browser preview"
194 data-preview-mini-player={tabId}200 data-preview-mini-player={tabId}
195 className="pointer-events-none absolute select-none"201 className="pointer-events-none absolute select-none"
196 style={{ left: frame.x, top: frame.y, width: frame.width, height: frame.height }}202 style={{
203 left: frame.x,
204 top: frame.y,
205 width: frame.width,
206 height: frame.height,
207 borderRadius: PREVIEW_MINI_PLAYER_CORNER_RADIUS,
208 }}
197 >209 >
198 <div className="group pointer-events-auto absolute right-2 top-2 z-[49] size-3">210 <div className="group pointer-events-auto absolute right-2 top-2 z-[49] size-3">
199 <div211 <div
...@@ -267,19 +279,19 @@ export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props...@@ -267,19 +279,19 @@ export function ThreadPreviewMiniPlayer({ threadRef, tabId, bottomInset }: Props
267 </div>279 </div>
268 </div>280 </div>
269281
270 <div className="absolute inset-0 z-[47] rounded-xl bg-muted shadow-2xl/35" />282 <div className="absolute inset-0 z-[47] rounded-[inherit] bg-muted shadow-2xl/35" />
271 <BrowserSurfaceSlot283 <BrowserSurfaceSlot
272 tabId={runtimeTabId}284 tabId={runtimeTabId}
273 visible={Boolean(desktopOverlay?.hasWebContents)}285 visible={Boolean(desktopOverlay?.hasWebContents)}
274 cornerRadius={12}286 cornerRadius={PREVIEW_MINI_PLAYER_CORNER_RADIUS}
275 zIndex={PREVIEW_MINI_PLAYER_WEBVIEW_Z_INDEX}287 zIndex={PREVIEW_MINI_PLAYER_WEBVIEW_Z_INDEX}
276 fitSourceContent288 fitSourceContent
277 layoutVersion={`${frame.x}:${frame.y}`}289 layoutVersion={`${frame.x}:${frame.y}`}
278 className="absolute inset-0"290 className="absolute inset-0"
279 />291 />
280 <div className="pointer-events-none absolute inset-0 z-[49] rounded-xl ring-1 ring-inset ring-border/80" />292 <div className="pointer-events-none absolute inset-0 z-[49] rounded-[inherit] ring-1 ring-inset ring-border/80" />
281 {!desktopOverlay?.hasWebContents ? (293 {!desktopOverlay?.hasWebContents ? (
282 <div className="pointer-events-none absolute inset-0 z-[49] flex items-center justify-center rounded-xl bg-muted text-xs text-muted-foreground">294 <div className="pointer-events-none absolute inset-0 z-[49] flex items-center justify-center rounded-[inherit] bg-muted text-xs text-muted-foreground">
283 Reconnecting preview…295 Reconnecting preview…
284 </div>296 </div>
285 ) : null}297 ) : null}