fix(ui): square mobile file tiles, files scroll clearance, apps-tab swipe guard

- 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) <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-06-19 13:25:26 -04:00
parent a0b80dd27d
commit cebbde7bde
3 changed files with 28 additions and 2 deletions

View File

@ -6,7 +6,7 @@
@click="handleClick"
>
<!-- Cover / Thumbnail area -->
<div class="cloud-grid-card-cover" :class="aspectClass">
<div class="cloud-grid-card-cover">
<!-- Image thumbnail -->
<img
v-if="isImage && thumbnailUrl && !imgFailed"
@ -155,7 +155,9 @@ const isCurrentlyPlaying = computed(() => 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'

View File

@ -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;
}

View File

@ -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