Unverified 提交 e9a079fb authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Device toggle (#2327)

#skip-bb fixes https://github.com/dyad-sh/dyad/issues/2318 <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Persist the selected device mode (desktop, tablet, mobile) in user settings so it stays after app rebuilds. Adds an end-to-end test to verify the mode persists through a rebuild. - **New Features** - PreviewIframe reads and writes previewDeviceMode via useSettings, replacing local state. - Added DeviceModeSchema and previewDeviceMode to UserSettingsSchema. - **Bug Fixes** - Stabilized e2e: added a persistence test, use po.clickRebuild(), and wait for preview loading to appear/disappear with a final assertion timeout. <sup>Written for commit c801c37c314413ba23c0174dc7bb401193826389. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: 's avatarClaude Opus 4.5 <noreply@anthropic.com>
上级 3b13f338
......@@ -76,4 +76,36 @@ test.describe("Toggle Screen Size Tests", () => {
);
expect(mobileWidth).toBe("375");
});
testSkipIfWindows(
"should persist device mode after rebuild",
async ({ po }) => {
test.setTimeout(Timeout.EXTRA_LONG * 2);
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 mobile mode
await deviceModeButton.click();
await po.page.locator('[aria-label="Mobile view"]').click();
await expect(previewIframe).toHaveAttribute("style", /width:\s*375px/);
// Trigger rebuild
await po.clickRebuild();
await expect(po.locateLoadingAppPreview()).toBeVisible();
await expect(po.locateLoadingAppPreview()).not.toBeVisible({
timeout: Timeout.EXTRA_LONG,
});
// Verify mobile mode persists after rebuild
await expect(previewIframe).toHaveAttribute("style", /width:\s*375px/, {
timeout: Timeout.LONG,
});
},
);
});
......@@ -60,10 +60,12 @@ import {
} from "@/components/ui/popover";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { useRunApp } from "@/hooks/useRunApp";
import { useSettings } from "@/hooks/useSettings";
import { useShortcut } from "@/hooks/useShortcut";
import { cn } from "@/lib/utils";
import { normalizePath } from "../../../shared/normalizePath";
import { showError } from "@/lib/toast";
import type { DeviceMode } from "@/lib/schemas";
import { AnnotatorOnlyForPro } from "./AnnotatorOnlyForPro";
import { useAttachments } from "@/hooks/useAttachments";
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
......@@ -178,6 +180,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
const { streamMessage } = useStreamChat();
const { routes: availableRoutes } = useParseRouter(selectedAppId);
const { restartApp } = useRunApp();
const { settings, updateSettings } = useSettings();
const { userBudget } = useUserBudgetInfo();
const isProMode = !!userBudget;
......@@ -212,8 +215,7 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
const [hasStaticText, setHasStaticText] = useState(false);
// Device mode state
type DeviceMode = "desktop" | "tablet" | "mobile";
const [deviceMode, setDeviceMode] = useState<DeviceMode>("desktop");
const deviceMode: DeviceMode = settings?.previewDeviceMode ?? "desktop";
const [isDevicePopoverOpen, setIsDevicePopoverOpen] = useState(false);
// Device configurations
......@@ -963,7 +965,8 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
data-testid="device-mode-button"
onClick={() => {
// Toggle popover open/close
if (isDevicePopoverOpen) setDeviceMode("desktop");
if (isDevicePopoverOpen)
updateSettings({ previewDeviceMode: "desktop" });
setIsDevicePopoverOpen(!isDevicePopoverOpen);
}}
className={cn(
......@@ -986,7 +989,9 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
value={deviceMode}
onValueChange={(value) => {
if (value) {
setDeviceMode(value as DeviceMode);
updateSettings({
previewDeviceMode: value as DeviceMode,
});
setIsDevicePopoverOpen(false);
}
}}
......
......@@ -235,6 +235,9 @@ export type ReleaseChannel = z.infer<typeof ReleaseChannelSchema>;
export const ZoomLevelSchema = z.enum(["90", "100", "110", "125", "150"]);
export type ZoomLevel = z.infer<typeof ZoomLevelSchema>;
export const DeviceModeSchema = z.enum(["desktop", "tablet", "mobile"]);
export type DeviceMode = z.infer<typeof DeviceModeSchema>;
export const SmartContextModeSchema = z.enum([
"balanced",
"conservative",
......@@ -294,6 +297,7 @@ export const UserSettingsSchema = z
defaultChatMode: ChatModeSchema.optional(),
acceptedCommunityCode: z.boolean().optional(),
zoomLevel: ZoomLevelSchema.optional(),
previewDeviceMode: DeviceModeSchema.optional(),
enableAutoFixProblems: z.boolean().optional(),
enableNativeGit: z.boolean().optional(),
......
......@@ -136,7 +136,6 @@
"version": "20.17.46",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.19.2"
}
......@@ -1528,7 +1527,6 @@
"version": "5.8.3",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论