fix(web): keep overlays below PWA status bar (#1124)

* fix codex session import merge (#1123)

修复 Codex 会话导入合并后列表为空的问题。

Fix Codex session import merge so a session is not detected as a duplicate of itself and deleted during merge.

Co-authored-by: LIUZHIRU <ryuu@fine-net.co.jp>

* fix(web): keep overlays below PWA status bar

Account for env(safe-area-inset-top) on fixed top banners, image
preview chrome, and session action menus so installed PWA mode no
longer draws controls under the OS status bar.

Fixes #1066

---------

Co-authored-by: Himehane <36065996+Himehane@users.noreply.github.com>
Co-authored-by: LIUZHIRU <ryuu@fine-net.co.jp>
This commit is contained in:
Zqqqqqq
2026-07-22 23:32:10 +08:00
committed by GitHub
co-authored by Himehane LIUZHIRU
parent a965b0ab21
commit cd25660658
8 changed files with 17 additions and 9 deletions
+1 -1
View File
@@ -240,7 +240,7 @@ export function ImagePreview(props: {
aria-modal="true"
aria-label={props.label}
>
<div className="flex items-center gap-2 border-b border-white/10 bg-black/50 px-3 py-2">
<div className="flex items-center gap-2 border-b border-white/10 bg-black/50 px-3 pb-2 pt-[calc(env(safe-area-inset-top)+0.5rem)]">
<div className="min-w-0 flex-1 truncate text-sm font-medium">{props.fileName}</div>
<button
type="button"
+1 -1
View File
@@ -10,7 +10,7 @@ export function OfflineBanner() {
}
return (
<div className="fixed top-0 left-0 right-0 bg-amber-500 text-white text-center py-2 text-sm font-medium z-50">
<div className="fixed top-0 left-0 right-0 bg-amber-500 text-white text-center pb-2 pt-[calc(env(safe-area-inset-top)+0.5rem)] text-sm font-medium z-50">
{t('offline.message')}
</div>
)
+3 -1
View File
@@ -117,7 +117,9 @@ describe('PwaUpdateBanner', () => {
</I18nProvider>,
)
expect(screen.getByTestId('pwa-update-banner')).toHaveClass('top-12')
expect(screen.getByTestId('pwa-update-banner')).toHaveClass(
'top-[calc(env(safe-area-inset-top)+3rem)]'
)
})
it('expands the rationale section when the disclosure is opened', () => {
+8 -2
View File
@@ -14,7 +14,9 @@ export function PwaUpdateBanner({ topClassName }: { topClassName?: string } = {}
return null
}
const topClass = topClassName ?? (isOnline ? 'top-2' : 'top-10')
const topClass = topClassName ?? (isOnline
? 'top-[calc(env(safe-area-inset-top)+0.5rem)]'
: 'top-[calc(env(safe-area-inset-top)+2.5rem)]')
return (
<div
@@ -68,6 +70,10 @@ export function PwaUpdateBannerWithStatusOffset({
Boolean(voice && voice.status === 'error' && voice.errorMessage)
return (
<PwaUpdateBanner topClassName={hasTopStatusBanner ? 'top-12' : undefined} />
<PwaUpdateBanner
topClassName={hasTopStatusBanner
? 'top-[calc(env(safe-area-inset-top)+3rem)]'
: undefined}
/>
)
}
+1 -1
View File
@@ -34,7 +34,7 @@ export function ReconnectingBanner({
}
return (
<div className="fixed top-0 left-0 right-0 bg-amber-500 text-white text-center py-2 text-sm font-medium z-50 flex items-center justify-center gap-2">
<div className="fixed top-0 left-0 right-0 bg-amber-500 text-white text-center pb-2 pt-[calc(env(safe-area-inset-top)+0.5rem)] text-sm font-medium z-50 flex items-center justify-center gap-2">
<span className="animate-spin h-4 w-4 border-2 border-white border-t-transparent rounded-full" />
{t('reconnecting.message')}
{reasonLabel ? <span className="opacity-90">({reasonLabel})</span> : null}
+1 -1
View File
@@ -289,7 +289,7 @@ export function SessionActionMenu(props: SessionActionMenuProps) {
const menuStyle: CSSProperties | undefined = menuPosition
? {
top: menuPosition.top,
top: `max(${menuPosition.top}px, calc(env(safe-area-inset-top) + 8px))`,
left: menuPosition.left,
transformOrigin: menuPosition.transformOrigin
}
+1 -1
View File
@@ -12,7 +12,7 @@ export function SyncingBanner({ isSyncing }: { isSyncing: boolean }) {
}
return (
<div className="fixed top-0 left-0 right-0 bg-[var(--app-banner-bg)] text-[var(--app-banner-text)] text-center py-2 text-sm font-medium z-50 flex items-center justify-center gap-2 border-b border-[var(--app-divider)]">
<div className="fixed top-0 left-0 right-0 bg-[var(--app-banner-bg)] text-[var(--app-banner-text)] text-center pb-2 pt-[calc(env(safe-area-inset-top)+0.5rem)] text-sm font-medium z-50 flex items-center justify-center gap-2 border-b border-[var(--app-divider)]">
<Spinner size="sm" label={null} className="text-[var(--app-banner-text)]" />
{t('syncing.title')}
</div>
+1 -1
View File
@@ -21,7 +21,7 @@ export function VoiceErrorBanner() {
}
return (
<div className="fixed top-0 left-0 right-0 bg-red-500 text-white text-center py-2 text-sm font-medium z-50 flex items-center justify-center border-b border-red-600">
<div className="fixed top-0 left-0 right-0 bg-red-500 text-white text-center pb-2 pt-[calc(env(safe-area-inset-top)+0.5rem)] text-sm font-medium z-50 flex items-center justify-center border-b border-red-600">
{voice.errorMessage}
</div>
)