From cebbde7bde6bcb514f1f971c538ff33dc7c57797 Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 19 Jun 2026 13:25:26 -0400 Subject: [PATCH] fix(ui): square mobile file tiles, files scroll clearance, apps-tab swipe guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Apps tab: a horizontal swipe that starts on an app icon no longer flips the top tab — it lets the app-page scroll / icon tap win (swipe empty space to change tab). Fixes the swipe conflict with two pages of apps. - Files: file cover tiles are forced square on mobile (aspect driven by CSS, not a Tailwind arbitrary class) so the grid is uniform and tappable. - Files: scroll container gets bottom safe-area + tab-bar padding so the last row clears the mobile back button / bottom nav. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../src/components/cloud/FileCardGrid.vue | 6 ++++-- neode-ui/src/style.css | 19 +++++++++++++++++++ neode-ui/src/views/Dashboard.vue | 5 +++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/neode-ui/src/components/cloud/FileCardGrid.vue b/neode-ui/src/components/cloud/FileCardGrid.vue index 330e1fc6..352a9176 100644 --- a/neode-ui/src/components/cloud/FileCardGrid.vue +++ b/neode-ui/src/components/cloud/FileCardGrid.vue @@ -6,7 +6,7 @@ @click="handleClick" > -
+
audioPlaying.value && currentSrc.value // Uniform card cover ratio across every file type so folders, images, videos // and documents all render at the same height in the grid (previously images/ // videos were square while folders were 4/3, giving a ragged, mismatched grid). -const aspectClass = computed(() => 'aspect-[4/3]') +// Aspect is now driven entirely by .cloud-grid-card-cover CSS (4/3 desktop, +// square on mobile) so the ratio is deterministic regardless of Tailwind layer +// ordering. const coverBg = computed(() => { if (props.item.isDir) return 'bg-amber-500/10' diff --git a/neode-ui/src/style.css b/neode-ui/src/style.css index 4677bdf6..90e919f1 100644 --- a/neode-ui/src/style.css +++ b/neode-ui/src/style.css @@ -1827,6 +1827,22 @@ html.modal-scroll-locked .dashboard-scroll-panel { } } +/* Mobile: square, tappable tiles + bottom clearance so the last row scrolls + above the tab bar / back button (matches .mobile-scroll-pad). */ +@media (max-width: 767px) { + .cloud-card-grid, + .cloud-file-list { + padding-bottom: calc( + var(--mobile-tab-bar-height, 88px) + + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)) + + var(--audio-player-height, 0px) + 24px + ); + } + .cloud-grid-card-cover { + aspect-ratio: 1 / 1; + } +} + .cloud-grid-card { display: flex; flex-direction: column; @@ -1856,6 +1872,9 @@ html.modal-scroll-locked .dashboard-scroll-panel { .cloud-grid-card-cover { position: relative; width: 100%; + /* Fallback aspect when the Tailwind aspect-[4/3] utility is unavailable, so + the cover never collapses to zero height. */ + aspect-ratio: 4 / 3; overflow: hidden; border-radius: 0.625rem; } diff --git a/neode-ui/src/views/Dashboard.vue b/neode-ui/src/views/Dashboard.vue index 34d85cc4..bc6b1580 100644 --- a/neode-ui/src/views/Dashboard.vue +++ b/neode-ui/src/views/Dashboard.vue @@ -290,14 +290,19 @@ function activeNetKey(): string { let touchStartX = 0 let touchStartY = 0 let touchStartTime = 0 +let swipeSuppressed = false function onContentTouchStart(e: TouchEvent) { const t = e.touches[0] if (!t) return + // Don't begin a tab swipe when the gesture starts on an app icon — let the + // icon handle the tap/long-press. Swiping anywhere else still changes tabs. + swipeSuppressed = !!(e.target instanceof Element && e.target.closest('.app-icon-item')) touchStartX = t.clientX touchStartY = t.clientY touchStartTime = e.timeStamp } function onContentTouchEnd(e: TouchEvent) { + if (swipeSuppressed) { swipeSuppressed = false; return } const t = e.changedTouches[0] if (!t) return const dx = t.clientX - touchStartX