🔧 Frontend review fixes & quality improvements

- Fixed race condition in apiCache TTL handling by passing ttl per-entry
- Removed double snapshot reassignment bug after switch cases in useRenderers.ts  
- Replaced `as any` cast on transport_state with runtime guard (isTransportState)
- Fixed invalidate() to preserve subscriptions instead of deleting them
- Migrated reactive(Map) → shallowRef + explicit reactivity triggers (triggerSnapshotReactive, triggerLoadingReactice)
- Added onUnmounted cleanup for debounce timer in useRenderer()
- Exposed resetSSE() to allow reinitialization after SSE reconnect
- Split deep watch in useTabs.ts into separate lightweight watches without {deep: true}
- Fixed swipe gesture to capture startX in onSwipeStart instead of using final clientX
- Added minimal JSON validation log for null responses (DEV mode only)
- Implemented ARIA labels on transport controls and volume slider
- Added UI notifications for network errors via uiStore.notifyError()
+ Removed obsolete toRaw() usage and import after shallowRef migration
- Added missing reactivity triggers for state_changed, queue_refreshing/updated cases
This commit is contained in:
2026-04-06 08:47:22 +02:00
parent 66ba31b12f
commit ac9cb3ef3f
9 changed files with 801 additions and 77 deletions

View File

@@ -68,6 +68,7 @@ async function handleNext() {
:disabled="isPlaying"
@click="handlePlay"
title="Lecture"
aria-label="Lecture"
>
<Play :size="20" />
</button>
@@ -77,6 +78,7 @@ async function handleNext() {
:disabled="isPaused || isStopped"
@click="handlePause"
title="Pause"
aria-label="Pause"
>
<Pause :size="20" />
</button>
@@ -86,6 +88,7 @@ async function handleNext() {
:disabled="isStopped"
@click="handleStop"
title="Stop"
aria-label="Arrêter"
>
<Square :size="20" />
</button>
@@ -95,6 +98,7 @@ async function handleNext() {
:disabled="!state?.queue_len"
@click="handleNext"
title="Suivant"
aria-label="Morceau suivant"
>
<SkipForward :size="20" />
</button>

View File

@@ -65,6 +65,7 @@ async function handleToggleMute() {
class="btn btn-icon"
@click="handleToggleMute"
:title="state?.mute ? 'Réactiver le son' : 'Couper le son'"
:aria-label="state?.mute ? 'Réactiver le son' : 'Couper le son'"
>
<VolumeX v-if="state?.mute" :size="20" />
<Volume2 v-else :size="20" />
@@ -78,6 +79,7 @@ async function handleToggleMute() {
@input="handleVolumeChange"
class="volume-slider"
:disabled="state?.mute ?? false"
aria-label="Volume"
/>
<span class="volume-value">{{ localVolume }}</span>