Compare commits

...

1 Commits

Author SHA1 Message Date
simosmik
f670f552ac fix: mobile viewport 2026-02-20 08:10:22 +00:00
4 changed files with 24 additions and 4 deletions

View File

@@ -24,7 +24,7 @@ export const CollapsibleSection: React.FC<CollapsibleSectionProps> = ({
}) => { }) => {
return ( return (
<details className={`relative group/details ${className}`} open={open}> <details className={`relative group/details ${className}`} open={open}>
<summary className="flex items-center gap-1.5 text-xs cursor-pointer py-0.5 select-none"> <summary className="flex items-center gap-1.5 text-xs cursor-pointer py-0.5 select-none group-open/details:sticky group-open/details:top-0 group-open/details:z-10 group-open/details:bg-background group-open/details:-mx-1 group-open/details:px-1">
<svg <svg
className="w-3 h-3 text-gray-400 dark:text-gray-500 transition-transform duration-150 group-open/details:rotate-90 flex-shrink-0" className="w-3 h-3 text-gray-400 dark:text-gray-500 transition-transform duration-150 group-open/details:rotate-90 flex-shrink-0"
fill="none" fill="none"

View File

@@ -62,8 +62,8 @@ export default function MainContentTitle({
</div> </div>
) : showChatNewSession ? ( ) : showChatNewSession ? (
<div className="min-w-0"> <div className="min-w-0">
<h2 className="text-sm font-semibold text-foreground leading-tight">{t('mainContent.newSession')}</h2> <h2 className="text-base font-semibold text-foreground leading-tight">{t('mainContent.newSession')}</h2>
<div className="text-[11px] text-muted-foreground truncate leading-tight">{selectedProject.displayName}</div> <div className="text-xs text-muted-foreground truncate leading-tight">{selectedProject.displayName}</div>
</div> </div>
) : ( ) : (
<div className="min-w-0"> <div className="min-w-0">

View File

@@ -62,6 +62,9 @@
--safe-area-inset-bottom: env(safe-area-inset-bottom); --safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left: env(safe-area-inset-left); --safe-area-inset-left: env(safe-area-inset-left);
/* Virtual keyboard height (set by JS for iOS/Safari fallback) */
--keyboard-height: 0px;
/* Mobile navigation dimensions - Single source of truth */ /* Mobile navigation dimensions - Single source of truth */
/* Floating nav: ~52px bar + 8px bottom margin + 12px px-3 top spacing */ /* Floating nav: ~52px bar + 8px bottom margin + 12px px-3 top spacing */
--mobile-nav-height: 52px; --mobile-nav-height: 52px;
@@ -166,12 +169,16 @@
overflow: hidden; overflow: hidden;
} }
/* Virtual keyboard offset — works in both PWA and regular mobile browsers */
.fixed.inset-0 {
bottom: max(env(keyboard-inset-bottom, 0px), var(--keyboard-height, 0px));
}
/* Adjust fixed inset positioning in PWA mode */ /* Adjust fixed inset positioning in PWA mode */
body.pwa-mode .fixed.inset-0 { body.pwa-mode .fixed.inset-0 {
top: var(--header-total-padding); top: var(--header-total-padding);
left: var(--safe-area-inset-left); left: var(--safe-area-inset-left);
right: var(--safe-area-inset-right); right: var(--safe-area-inset-right);
bottom: 0;
} }
/* Global transition defaults */ /* Global transition defaults */

View File

@@ -7,6 +7,19 @@ import 'katex/dist/katex.min.css'
// Initialize i18n // Initialize i18n
import './i18n/config.js' import './i18n/config.js'
// Tell the browser to overlay the virtual keyboard instead of resizing the viewport (PWA)
if ('virtualKeyboard' in navigator) {
navigator.virtualKeyboard.overlaysContent = true;
} else if (window.visualViewport) {
// iOS/Safari fallback: track keyboard height via visualViewport
const viewport = window.visualViewport;
const updateKeyboardHeight = () => {
const keyboardHeight = Math.max(0, window.innerHeight - viewport.height);
document.documentElement.style.setProperty('--keyboard-height', `${keyboardHeight}px`);
};
viewport.addEventListener('resize', updateKeyboardHeight);
}
// Clean up stale service workers on app load to prevent caching issues after builds // Clean up stale service workers on app load to prevent caching issues after builds
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(registrations => { navigator.serviceWorker.getRegistrations().then(registrations => {