From 517ce5134dee2f1de6c85661de91239aed01d952 Mon Sep 17 00:00:00 2001
From: Mohamed Aziz Mejri
Date: Thu, 23 Oct 2025 05:14:39 +0100
Subject: [PATCH] feat: allow toggling between screen sizes (#1582)
Implement the feature requested in issue #251 that allows users to
toggle between screen sizes
---
e2e-tests/toggle_screen_sizes.spec.ts | 79 ++++++++++
.../preview_panel/PreviewIframe.tsx | 138 ++++++++++++++++--
2 files changed, 204 insertions(+), 13 deletions(-)
create mode 100644 e2e-tests/toggle_screen_sizes.spec.ts
diff --git a/e2e-tests/toggle_screen_sizes.spec.ts b/e2e-tests/toggle_screen_sizes.spec.ts
new file mode 100644
index 0000000..44e2218
--- /dev/null
+++ b/e2e-tests/toggle_screen_sizes.spec.ts
@@ -0,0 +1,79 @@
+import { test, testSkipIfWindows, Timeout } from "./helpers/test_helper";
+import { expect } from "@playwright/test";
+
+test.describe("Toggle Screen Size Tests", () => {
+ async function setupApp(po: any) {
+ await po.setUp({ autoApprove: true });
+ await po.sendPrompt("tc=write-index");
+
+ const iframe = po.getPreviewIframeElement();
+ const frame = await iframe.contentFrame();
+
+ await expect(frame.getByText("Testing:write-index!")).toBeVisible({
+ timeout: Timeout.EXTRA_LONG,
+ });
+ }
+
+ testSkipIfWindows(
+ "should open and close device mode popover",
+ async ({ po }) => {
+ test.setTimeout(Timeout.EXTRA_LONG * 1.5);
+ await setupApp(po);
+
+ // Click the device mode button to open popover
+ const deviceModeButton = po.page.locator(
+ '[data-testid="device-mode-button"]',
+ );
+ await deviceModeButton.click();
+
+ // Verify popover is visible with device options
+ const originalButton = po.page.locator('[aria-label="Desktop view"]');
+ await expect(originalButton).toBeVisible();
+
+ // Close popover by clicking the button again
+ await deviceModeButton.click();
+
+ // Verify popover is closed
+ await expect(originalButton).toBeHidden();
+ },
+ );
+
+ testSkipIfWindows("should switch between device modes", async ({ po }) => {
+ test.setTimeout(Timeout.EXTRA_LONG * 1.5);
+ await setupApp(po);
+
+ const deviceModeButton = po.page.locator(
+ '[data-testid="device-mode-button"]',
+ );
+
+ const previewIframe = po.page.locator(
+ '[data-testid="preview-iframe-element"]',
+ );
+
+ // Switch to tablet mode
+ await deviceModeButton.click();
+ await po.page.locator('[aria-label="Tablet view"]').click();
+
+ // Wait for the iframe width to change to tablet size (768px)
+ await expect(previewIframe).toHaveAttribute("style", /width:\s*768px/);
+
+ // Verify iframe has tablet dimensions
+ const tabletWidth = await previewIframe.evaluate((el: HTMLIFrameElement) =>
+ el.style.width.replace("px", ""),
+ );
+ expect(tabletWidth).toBe("768");
+
+ // Switch to mobile mode
+ await deviceModeButton.click();
+ await po.page.locator('[aria-label="Mobile view"]').click();
+
+ // Wait for the iframe width to change to mobile size (375px)
+ await expect(previewIframe).toHaveAttribute("style", /width:\s*375px/);
+
+ // Verify iframe has mobile dimensions
+ const mobileWidth = await previewIframe.evaluate((el: HTMLIFrameElement) =>
+ el.style.width.replace("px", ""),
+ );
+ expect(mobileWidth).toBe("375");
+ });
+});
diff --git a/src/components/preview_panel/PreviewIframe.tsx b/src/components/preview_panel/PreviewIframe.tsx
index 37bb350..6d019f8 100644
--- a/src/components/preview_panel/PreviewIframe.tsx
+++ b/src/components/preview_panel/PreviewIframe.tsx
@@ -19,6 +19,10 @@ import {
ChevronRight,
MousePointerClick,
Power,
+ MonitorSmartphone,
+ Monitor,
+ Tablet,
+ Smartphone,
} from "lucide-react";
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { IpcClient } from "@/ipc/ipc_client";
@@ -39,6 +43,12 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { useRunApp } from "@/hooks/useRunApp";
import { useShortcut } from "@/hooks/useShortcut";
import { cn } from "@/lib/utils";
@@ -165,6 +175,17 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
const iframeRef = useRef(null);
const [isPicking, setIsPicking] = useState(false);
+ // Device mode state
+ type DeviceMode = "desktop" | "tablet" | "mobile";
+ const [deviceMode, setDeviceMode] = useState("desktop");
+ const [isDevicePopoverOpen, setIsDevicePopoverOpen] = useState(false);
+
+ // Device configurations
+ const deviceWidthConfig = {
+ tablet: 768,
+ mobile: 375,
+ };
+
//detect if the user is using Mac
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
@@ -547,6 +568,85 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
>
+
+ {/* Device Mode Button */}
+
+
+
+
+ e.preventDefault()}
+ onInteractOutside={(e) => e.preventDefault()}
+ >
+
+ {
+ if (value) {
+ setDeviceMode(value as DeviceMode);
+ setIsDevicePopoverOpen(false);
+ }
+ }}
+ variant="outline"
+ >
+ {/* Tooltips placed inside items instead of wrapping
+ to avoid asChild prop merging that breaks highlighting */}
+
+
+
+
+
+
+
+
+ Desktop
+
+
+
+
+
+
+
+
+
+
+
+ Tablet
+
+
+
+
+
+
+
+
+
+
+
+ Mobile
+
+
+
+
+
+
+
@@ -572,19 +672,31 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
) : (
-