Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
B
bit-pm
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
燕伟桐
bit-pm
Commits
517ce513
Unverified
提交
517ce513
authored
10月 23, 2025
作者:
Mohamed Aziz Mejri
提交者:
GitHub
10月 23, 2025
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: allow toggling between screen sizes (#1582)
Implement the feature requested in issue #251 that allows users to toggle between screen sizes
上级
b1095b79
显示空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
191 行增加
和
0 行删除
+191
-0
toggle_screen_sizes.spec.ts
e2e-tests/toggle_screen_sizes.spec.ts
+79
-0
PreviewIframe.tsx
src/components/preview_panel/PreviewIframe.tsx
+112
-0
没有找到文件。
e2e-tests/toggle_screen_sizes.spec.ts
0 → 100644
浏览文件 @
517ce513
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"
);
});
});
src/components/preview_panel/PreviewIframe.tsx
浏览文件 @
517ce513
...
...
@@ -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
<
HTMLIFrameElement
>
(
null
);
const
[
isPicking
,
setIsPicking
]
=
useState
(
false
);
// Device mode state
type
DeviceMode
=
"desktop"
|
"tablet"
|
"mobile"
;
const
[
deviceMode
,
setDeviceMode
]
=
useState
<
DeviceMode
>
(
"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 }) => {
>
<
ExternalLink
size=
{
16
}
/>
</
button
>
{
/* Device Mode Button */
}
<
Popover
open=
{
isDevicePopoverOpen
}
modal=
{
false
}
>
<
PopoverTrigger
asChild
>
<
button
data
-
testid=
"device-mode-button"
onClick=
{
()
=>
{
// Toggle popover open/close
if
(
isDevicePopoverOpen
)
setDeviceMode
(
"desktop"
);
setIsDevicePopoverOpen
(
!
isDevicePopoverOpen
);
}
}
className=
{
cn
(
"p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 dark:text-gray-300"
,
deviceMode
!==
"desktop"
&&
"bg-gray-200 dark:bg-gray-700"
,
)
}
title=
"Device Mode"
>
<
MonitorSmartphone
size=
{
16
}
/>
</
button
>
</
PopoverTrigger
>
<
PopoverContent
className=
"w-auto p-2"
onOpenAutoFocus=
{
(
e
)
=>
e
.
preventDefault
()
}
onInteractOutside=
{
(
e
)
=>
e
.
preventDefault
()
}
>
<
TooltipProvider
>
<
ToggleGroup
type=
"single"
value=
{
deviceMode
}
onValueChange=
{
(
value
)
=>
{
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 */
}
<
ToggleGroupItem
value=
"desktop"
aria
-
label=
"Desktop view"
>
<
Tooltip
>
<
TooltipTrigger
asChild
>
<
span
className=
"flex items-center justify-center"
>
<
Monitor
size=
{
16
}
/>
</
span
>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
Desktop
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
ToggleGroupItem
>
<
ToggleGroupItem
value=
"tablet"
aria
-
label=
"Tablet view"
>
<
Tooltip
>
<
TooltipTrigger
asChild
>
<
span
className=
"flex items-center justify-center"
>
<
Tablet
size=
{
16
}
className=
"scale-x-130"
/>
</
span
>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
Tablet
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
ToggleGroupItem
>
<
ToggleGroupItem
value=
"mobile"
aria
-
label=
"Mobile view"
>
<
Tooltip
>
<
TooltipTrigger
asChild
>
<
span
className=
"flex items-center justify-center"
>
<
Smartphone
size=
{
16
}
/>
</
span
>
</
TooltipTrigger
>
<
TooltipContent
>
<
p
>
Mobile
</
p
>
</
TooltipContent
>
</
Tooltip
>
</
ToggleGroupItem
>
</
ToggleGroup
>
</
TooltipProvider
>
</
PopoverContent
>
</
Popover
>
</
div
>
</
div
>
...
...
@@ -572,6 +672,12 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
</
p
>
</
div
>
)
:
(
<
div
className=
{
cn
(
"w-full h-full"
,
deviceMode
!==
"desktop"
&&
"flex justify-center"
,
)
}
>
<
iframe
sandbox=
"allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-orientation-lock allow-pointer-lock allow-presentation allow-downloads"
data
-
testid=
"preview-iframe-element"
...
...
@@ -582,9 +688,15 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
key=
{
reloadKey
}
title=
{
`Preview for App ${selectedAppId}`
}
className=
"w-full h-full border-none bg-white dark:bg-gray-950"
style=
{
deviceMode
==
"desktop"
?
{}
:
{
width
:
`${deviceWidthConfig[deviceMode]}px`
}
}
src=
{
appUrl
}
allow=
"clipboard-read; clipboard-write; fullscreen; microphone; camera; display-capture; geolocation; autoplay; picture-in-picture"
/>
</
div
>
)
}
</
div
>
</
div
>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论