🎨 Improve mobile safe-area support and refine UPnP/Chromecast rendering

- Add viewport-fit=cover to HTML meta for iOS safe-area support
- Extend bottom drawers and tab bar into system navigation area using env(safe_area_inset_bottom)
- Adjust queue drawer transforms to account for safe-area padding
+ Add enrich_position_from_queue() helper and integrate across UPnP, Arylic TCP,
  LinkPlay & Chromecast backends to ensure queue-authoritative metadata
+ Add transient error retry logic for UPnP control actions (2 retries, 300ms delay)
+ Separate timeouts: short poll timeout for GetTransportInfo/GetPosition (3s),
  longer actiontimeout SetAVTURI/SetNext... for slow devices
+ Remove duplicate continuous-stream detection from play_uri() methods (now handled centrally)
- Bump version to 0.3.49
This commit is contained in:
2026-04-13 04:04:24 +02:00
parent 9ad7d7454d
commit 9b5a028f3f
17 changed files with 241 additions and 409 deletions

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>webapp</title>
</head>
<body>

View File

@@ -257,6 +257,9 @@
.drawer-footer {
padding: var(--spacing-md);
/* On mobile, extend into the safe-area so the footer doesn't sit under
the system navigation bar / home indicator. */
padding-bottom: max(var(--spacing-md), env(safe-area-inset-bottom, 0px));
border-top: 1px solid rgba(255, 255, 255, 0.1);
flex-shrink: 0;
}

View File

@@ -147,7 +147,10 @@ function handleRendererDrawerClick() {
align-items: center;
gap: var(--spacing-md);
height: 72px;
padding: 0 var(--spacing-md);
/* Extend background into the system navigation bar area (iOS home indicator,
Android gesture bar). Content stays in the 72px zone; only the visual
background bleeds into the safe-area below. */
padding: 0 var(--spacing-md) env(safe-area-inset-bottom, 0px);
background: rgba(22, 22, 32, 0.96);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
@@ -323,7 +326,7 @@ function handleRendererDrawerClick() {
@media (max-width: 768px) {
.bottom-bar {
height: 64px;
padding: 0 var(--spacing-sm);
padding: 0 var(--spacing-sm) env(safe-area-inset-bottom, 0px);
gap: var(--spacing-sm);
}

View File

@@ -239,10 +239,11 @@ async function handleQueueItemClick(item: QueueItem) {
-webkit-backdrop-filter: blur(8px);
border-top: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 -4px 32px rgba(0, 0, 0, 0.4);
/* Fermé: caché sauf le toggle (56px) qui dépasse au-dessus de la BottomTabBar (64px) */
transform: translateY(calc(100% - 56px - 64px));
/* Fermé: caché sauf le toggle (56px) qui dépasse au-dessus de la BottomTabBar.
La BottomTabBar fait 64px + env(safe-area-inset-bottom) de padding. */
transform: translateY(calc(100% - 56px - 64px - env(safe-area-inset-bottom, 0px)));
transition: transform 0.3s ease;
z-index: 95; /* Au-dessus de la BottomTabBar (z-index: 100) */
z-index: 95; /* En dessous de la BottomTabBar (z-index: 100) */
max-height: 70vh;
display: flex;
flex-direction: column;
@@ -251,8 +252,8 @@ async function handleQueueItemClick(item: QueueItem) {
}
.queue-drawer.open {
/* Ouvert: remonte mais s'arrête à 64px du bas pour laisser la BottomTabBar accessible */
transform: translateY(64px);
/* Ouvert: remonte juste au-dessus de la BottomTabBar (64px + safe area) */
transform: translateY(calc(64px + env(safe-area-inset-bottom, 0px)));
pointer-events: auto; /* Ouvert: capture les clics */
}
@@ -284,6 +285,8 @@ async function handleQueueItemClick(item: QueueItem) {
max-height: calc(70vh - 56px);
overflow-y: auto;
padding: var(--spacing-md);
/* Ensure the last item clears the safe-area / system nav bar */
padding-bottom: max(var(--spacing-md), env(safe-area-inset-bottom, 0px));
}
.queue-drawer-backdrop {