feat: mobile UI overhaul — iPhone-style app grid, icon-only tab bar, fullscreen app sessions

- Add AppIconGrid: 4-column swipeable icon grid with page dots for My Apps on mobile
- Tab bar: remove text labels, square icon-only buttons (w-14 h-14), doubled padding
- Hide tab bar and top context tabs when app session is open
- App session header hidden on mobile, replaced with floating glass close button
- App sessions now render fullscreen on mobile without nav chrome

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-30 21:03:00 +01:00
co-authored by Claude Opus 4.6
parent 7288bff6e0
commit 6bd515cb82
6 changed files with 357 additions and 15 deletions
@@ -1,7 +1,7 @@
<template>
<!-- Persistent Mobile Tabs for Apps/Marketplace -->
<div
v-if="showAppsTabs"
v-if="showAppsTabs && !isAppSessionActive"
class="md:hidden fixed top-0 left-0 right-0 z-40 px-4 pt-4 pb-2 glass-piece"
:class="{ 'glass-throw-mobile-tabs': showZoomIn }"
style="background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); transform: translateZ(0);"
@@ -28,7 +28,7 @@
<!-- Persistent Mobile Tabs for Network/Cloud -->
<div
v-if="showNetworkTabs"
v-if="showNetworkTabs && !isAppSessionActive"
class="md:hidden fixed top-0 left-0 right-0 z-40 px-4 pt-4 pb-2 glass-piece"
:class="{ 'glass-throw-mobile-tabs-2': showZoomIn }"
style="background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); transform: translateZ(0);"
@@ -58,8 +58,9 @@
</div>
</div>
<!-- Mobile Bottom Tab Bar -->
<!-- Mobile Bottom Tab Bar (hidden when app is open fullscreen) -->
<nav
v-if="!isAppSessionActive"
ref="mobileTabBar"
data-mobile-tab-bar
:aria-label="t('dashboard.mobileNav')"
@@ -74,7 +75,7 @@
:to="item.path"
aria-current-value="page"
@click="appLauncher.closePanel()"
class="flex flex-col items-center justify-center w-full py-1.5 rounded-lg text-white/70 transition-all duration-300 relative z-10 gap-0.5"
class="flex items-center justify-center w-14 h-14 rounded-xl text-white/70 transition-all duration-300 relative z-10"
:class="{
'nav-tab-active': item.isCombined
? (item.path === '/dashboard/apps'
@@ -99,17 +100,15 @@
:d="path"
/>
</svg>
<span class="text-[10px] leading-tight">{{ item.label }}</span>
</RouterLink>
<!-- Chat launcher -->
<button
@click="router.push('/dashboard/chat')"
class="chat-launcher-btn-mobile flex flex-col items-center justify-center w-full py-1.5 rounded-lg transition-all duration-300 relative z-10 gap-0.5"
class="chat-launcher-btn-mobile flex items-center justify-center w-14 h-14 rounded-xl transition-all duration-300 relative z-10"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path v-for="(path, index) in getIconPath('chat')" :key="index" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" :d="path" />
</svg>
<span class="text-[10px] leading-tight">AIUI</span>
</button>
</div>
</nav>
@@ -141,6 +140,11 @@ const uiMode = useUIModeStore()
const mobileTabBar = ref<HTMLElement | null>(null)
// Hide tab bar when an app session is open (fullscreen on mobile)
const isAppSessionActive = computed(() => {
return route.name === 'app-session' || !!appLauncher.panelAppId
})
// Show persistent tabs for Apps/Marketplace on mobile
const showAppsTabs = computed(() => {
if (typeof window === 'undefined') return false