feat: Discover view, Fleet dashboard, MeshMap, type fixes

- New Discover.vue (app store redesign)
- Fleet.vue dashboard for .228
- MeshMap.vue component
- Fixed Discover.vue type errors (unused var, type predicate)
- Various UI updates (Apps, Dashboard, Marketplace, Mesh, Web5)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-19 16:12:01 +00:00
co-authored by Claude Opus 4.6
parent 9b4aa712f2
commit f20f0650cf
18 changed files with 3067 additions and 174 deletions
+15 -4
View File
@@ -6,7 +6,7 @@
<div class="hidden md:flex items-center gap-4">
<div class="mode-switcher flex-shrink-0">
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'apps' }" @click="activeTab = 'apps'; router.replace({ query: {} })">My Apps</button>
<RouterLink to="/dashboard/marketplace" class="mode-switcher-btn">App Store</RouterLink>
<RouterLink to="/dashboard/discover" class="mode-switcher-btn">App Store</RouterLink>
<button class="mode-switcher-btn" :class="{ 'mode-switcher-btn-active': activeTab === 'services' }" @click="activeTab = 'services'; router.replace({ query: { tab: 'services' } })">Services</button>
</div>
<div v-if="activeTab === 'apps' && categoriesWithApps.length > 1" class="mode-switcher flex-shrink-0">
@@ -100,7 +100,8 @@
:data-controller-launch="canLaunch(pkg) ? '' : undefined"
tabindex="0"
role="link"
class="glass-card card-stagger p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden"
class="glass-card p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden"
:class="{ 'card-stagger': showStagger }"
:style="{ '--stagger-index': index }"
@click="goToApp(id as string)"
@keydown.enter="goToApp(id as string)"
@@ -307,21 +308,30 @@
</div>
</template>
<script lang="ts">
// Module-level — persists across component unmount/remount within same session
// Prevents stagger animation replaying every time user navigates back to Apps
let appsAnimationDone = false
</script>
<script setup lang="ts">
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
import { useRouter, useRoute, RouterLink } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '../stores/app'
const { t } = useI18n()
import { useAppLauncherStore } from '../stores/appLauncher'
import { PackageState, type PackageDataEntry } from '../types/api'
import { useModalKeyboard } from '@/composables/useModalKeyboard'
const { t } = useI18n()
const router = useRouter()
const route = useRoute()
const store = useAppStore()
// Only stagger-animate on first mount — skip on revisits
const showStagger = !appsAnimationDone
// Tabs — support ?tab=services from Marketplace link
const activeTab = ref<'apps' | 'services'>(
route.query.tab === 'services' ? 'services' : 'apps'
@@ -406,6 +416,7 @@ const connectionError = ref('')
let connectionTimer: ReturnType<typeof setTimeout> | undefined
onMounted(() => {
appsAnimationDone = true
if (!store.isConnected) {
connectionTimer = setTimeout(() => {
if (!store.isConnected && sortedPackageEntries.value.length === 0) {