fix: resolve content clipping on mobile by moving tab padding to scroll container

Moves dynamic pt-20/pt-40 padding from perspective-container-wrapper (which
shrank the content area) to the inner scroll container via computed style.
Removes spacer divs in CloudFolder, AppDetails, MarketplaceAppDetails.
Reduces excessive bottom padding in Marketplace. Hides Cloud/Network tabs
in CloudFolder detail view. Teleports mobile back buttons to body to escape
CSS transform containing block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-05 10:14:10 +00:00
parent 255b52eb6d
commit 11cee9dc70
9 changed files with 131 additions and 253 deletions

View File

@@ -180,7 +180,7 @@
background: transparent;
}
/* On mobile, leave room for close button + tab bar below AIUI */
/* On mobile, pad iframe so AIUI content ends above the tab bar */
@media (max-width: 767px) {
.chat-iframe-mobile {
padding-bottom: calc(var(--mobile-tab-bar-height, 72px) + 52px);

View File

@@ -8,16 +8,18 @@
{{ backButtonText }}
</button>
<!-- Mobile Full-Width Back Button -->
<button
@click="goBack"
class="md:hidden mobile-back-btn glass-button px-6 py-3 rounded-lg font-medium shadow-2xl flex items-center justify-center gap-2"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<span>{{ backButtonText }}</span>
</button>
<!-- Mobile Full-Width Back Button (teleported to escape CSS transform containing block) -->
<Teleport to="body">
<button
@click="goBack"
class="md:hidden mobile-back-btn glass-button px-6 py-3 rounded-lg font-medium shadow-2xl flex items-center justify-center gap-2"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<span>{{ backButtonText }}</span>
</button>
</Teleport>
<div v-if="pkg">
<!-- Compact Hero Section -->
@@ -371,9 +373,6 @@
<p class="text-white/70">The requested application could not be found</p>
</div>
<!-- Spacer for mobile back button -->
<div class="md:hidden h-[calc(var(--mobile-tab-bar-height,_64px)+96px)]"></div>
<!-- Uninstall Confirmation Modal -->
<Transition name="modal">
<div

View File

@@ -54,21 +54,6 @@
</div>
</div>
<!-- Mobile close bar (teleported to escape CSS transform containing block) -->
<Teleport to="body">
<div class="md:hidden mobile-back-btn flex items-center justify-center">
<button
class="w-full glass-button px-6 py-2.5 rounded-lg font-medium flex items-center justify-center gap-2 text-sm"
style="background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);"
@click="closeChat"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
Close Chat
</button>
</div>
</Teleport>
</div>
</template>

View File

@@ -9,16 +9,18 @@
Back to Cloud
</button>
<!-- Mobile Back Button -->
<button
@click="goBack"
class="md:hidden mobile-back-btn glass-button px-6 py-3 rounded-lg font-medium shadow-2xl flex items-center justify-center gap-2"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<span>Back to Cloud</span>
</button>
<!-- Mobile Back Button (teleported to escape CSS transform containing block) -->
<Teleport to="body">
<button
@click="goBack"
class="md:hidden mobile-back-btn glass-button px-6 py-3 rounded-lg font-medium shadow-2xl flex items-center justify-center gap-2"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<span>Back to Cloud</span>
</button>
</Teleport>
<!-- Folder Header -->
<div class="flex items-center justify-between">
@@ -150,8 +152,6 @@
/>
</div>
<!-- Spacer for mobile back button -->
<div class="md:hidden h-[calc(var(--mobile-tab-bar-height,_64px)+96px)]"></div>
</div>
</template>

View File

@@ -255,7 +255,7 @@
</div>
</div>
<div class="perspective-container-wrapper glass-piece" :class="{ 'pt-40': showAppsTabs && showNetworkTabs, 'pt-20': showAppsTabs !== showNetworkTabs, 'glass-throw-content': showZoomIn && !isHomeRoute }">
<div class="perspective-container-wrapper glass-piece" :class="{ 'glass-throw-content': showZoomIn && !isHomeRoute }">
<div class="perspective-container">
<RouterView v-slot="{ Component, route }">
<Transition :name="getTransitionName(route)">
@@ -269,11 +269,12 @@
<div
v-else
:class="[
'px-4 pt-4 pb-28 md:px-8 md:pt-8 md:pb-24 overflow-y-auto h-full',
'px-4 pt-4 md:pt-8 md:px-8 overflow-y-auto h-full',
needsMobileBackButtonSpace
? 'pb-[calc(var(--mobile-tab-bar-height,_72px)+96px)] md:pb-24'
: undefined
? 'pb-[calc(var(--mobile-tab-bar-height,_72px)+96px)] md:pb-8'
: 'pb-4 md:pb-8'
]"
:style="mobileTabPaddingTop ? { paddingTop: (mobileTabPaddingTop + 16) + 'px' } : undefined"
>
<component :is="Component" class="view-container" />
</div>
@@ -451,9 +452,18 @@ const showAppsTabs = computed(() => {
const showNetworkTabs = computed(() => {
if (typeof window === 'undefined') return false
if (window.innerWidth >= 768) return false
if (route.name === 'cloud-folder') return false
return route.path.includes('/server') || route.path.includes('/cloud')
})
// Top padding for content div to clear fixed mobile tab overlays
const mobileTabPaddingTop = computed(() => {
if (typeof window === 'undefined' || window.innerWidth >= 768) return 0
if (showAppsTabs.value && showNetworkTabs.value) return 160
if (showAppsTabs.value || showNetworkTabs.value) return 80
return 0
})
function updateTabBarHeight() {
if (typeof window === 'undefined') return
if (mobileTabBar.value) {

View File

@@ -1,6 +1,6 @@
<template>
<div>
<div class="mb-4 md:mb-8">
<div class="mb-4 md:mb-8 flex items-start justify-between gap-4">
<div class="min-h-[4.5rem]">
<h1 class="text-3xl font-bold text-white mb-2 drop-shadow-[0_2px_8px_rgba(0,0,0,0.6)]">
{{ line1Text }}<span v-if="showCaretLine1" class="typing-caret"></span>
@@ -9,11 +9,24 @@
{{ line2Text }}<span v-if="showCaretLine2" class="typing-caret"></span>
</p>
</div>
<!-- Desktop: tabs inline with header -->
<div
v-if="!uiMode.isChat"
class="hidden md:flex mode-switcher flex-shrink-0 transition-opacity duration-500"
:class="{ 'opacity-0 pointer-events-none': showWelcomeBlock && !animateCards }"
>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': homeTab === 'dashboard' }" @click="homeTab = 'dashboard'">Dashboard</button>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': homeTab === 'setup' }" @click="homeTab = 'setup'">Setup</button>
</div>
</div>
<!-- Tab bar + content (all non-chat modes) -->
<template v-if="!uiMode.isChat">
<div class="mode-switcher mb-6 max-w-xs">
<!-- Mobile: full-width tabs -->
<div
class="md:hidden mode-switcher mb-6 w-full transition-opacity duration-500"
:class="{ 'opacity-0 pointer-events-none': showWelcomeBlock && !animateCards }"
>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': homeTab === 'dashboard' }" @click="homeTab = 'dashboard'">Dashboard</button>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': homeTab === 'setup' }" @click="homeTab = 'setup'">Setup</button>
</div>

View File

@@ -1,7 +1,7 @@
<template>
<div class="marketplace-container flex flex-col h-full overflow-hidden -mt-4 md:mt-0">
<div class="marketplace-container flex flex-col h-full overflow-hidden md:-mt-4">
<!-- Fixed Header Section -->
<div class="flex-shrink-0 -mt-4 md:mt-0">
<div class="flex-shrink-0 md:-mt-4">
<!-- Installation Progress Banner - Multiple Apps -->
<div v-if="installingApps.size > 0" class="mb-6 space-y-3">
<div
@@ -106,19 +106,19 @@
/>
</div>
<!-- Search Bar (Mobile - placeholder for later) -->
<div class="md:hidden mb-6">
<!-- Search Bar (Mobile) -->
<div class="md:hidden mb-4">
<input
v-model="searchQuery"
type="text"
placeholder="Search apps..."
class="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-white/40 transition-colors"
class="w-full px-4 py-3 md:py-2 bg-white/10 border border-white/20 rounded-lg text-white placeholder-white/50 focus:outline-none focus:border-white/40 transition-colors"
/>
</div>
</div>
<!-- Scrollable Apps Section -->
<div class="flex-1 overflow-y-auto pr-2 -mr-2 pb-48">
<div class="flex-1 overflow-y-auto pr-2 -mr-2 pb-4">
<!-- Apps Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div
@@ -203,19 +203,18 @@
</div>
<!-- End Scrollable Apps Section -->
<!-- Floating Filter Button (Mobile only) -->
<button
@click="showFilterModal = true"
class="md:hidden fixed right-4 z-40 w-14 h-14 rounded-full glass-button flex items-center justify-center shadow-2xl"
:style="{
bottom: bottomPosition,
filter: 'drop-shadow(0 10px 25px rgba(0, 0, 0, 0.5))'
}"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
</button>
<!-- Floating Filter Button (teleported to escape CSS transform containing block) -->
<Teleport to="body">
<button
@click="showFilterModal = true"
class="md:hidden fixed right-4 z-40 w-14 h-14 rounded-full glass-button flex items-center justify-center shadow-2xl mobile-back-btn"
style="left: auto;"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
</button>
</Teleport>
<!-- Filter Modal (Mobile only) -->
<Transition name="modal">
@@ -304,13 +303,11 @@ import { useRouter } from 'vue-router'
import { useAppStore } from '@/stores/app'
import { rpcClient } from '@/api/rpc-client'
import { useMarketplaceApp } from '@/composables/useMarketplaceApp'
import { useMobileBackButton } from '@/composables/useMobileBackButton'
import { useModalKeyboard } from '@/composables/useModalKeyboard'
const router = useRouter()
const store = useAppStore()
const { setCurrentApp } = useMarketplaceApp()
const { bottomPosition } = useMobileBackButton()
// Category state
const selectedCategory = ref('all')

View File

@@ -320,8 +320,6 @@
<p class="text-white/70">The requested application could not be found in the marketplace</p>
</div>
<!-- Spacer for mobile back button -->
<div class="md:hidden h-[calc(var(--mobile-tab-bar-height,_64px)+96px)]"></div>
</div>
</template>