feat: cloud native file browser, settings Claude auth, deploy hardening
- Add native Cloud file browser with FileBrowser API integration - Add cloud store, filebrowser-client, useAudioPlayer, useFileType composables - Add Cloud components: FileGrid, FileCard, FileCardGrid, CloudToolbar - Add Claude authentication section to Settings with OAuth status check - Harden deploy script to preserve /aiui/ and claude-login.html - Add nginx proxies for btcpay, homeassistant, filebrowser (HTTPS block) - Add app configs for filebrowser, searxng, penpot in package.rs - Update goal progress tracking with app aliases - Improve mobile back button composable with ResizeObserver - Update various views with cloud integration and UI refinements Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
173bf8fc0f
commit
d7ff678e9d
@@ -0,0 +1,82 @@
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const audio = ref<HTMLAudioElement | null>(null)
|
||||
const currentSrc = ref<string | null>(null)
|
||||
const currentName = ref('')
|
||||
const playing = ref(false)
|
||||
const currentTime = ref(0)
|
||||
const duration = ref(0)
|
||||
|
||||
function play(src: string, name: string) {
|
||||
if (!audio.value) {
|
||||
audio.value = new Audio()
|
||||
audio.value.addEventListener('timeupdate', () => {
|
||||
currentTime.value = audio.value?.currentTime ?? 0
|
||||
})
|
||||
audio.value.addEventListener('loadedmetadata', () => {
|
||||
duration.value = audio.value?.duration ?? 0
|
||||
})
|
||||
audio.value.addEventListener('ended', () => {
|
||||
playing.value = false
|
||||
})
|
||||
audio.value.addEventListener('pause', () => {
|
||||
playing.value = false
|
||||
})
|
||||
audio.value.addEventListener('play', () => {
|
||||
playing.value = true
|
||||
})
|
||||
}
|
||||
|
||||
if (currentSrc.value === src && playing.value) {
|
||||
audio.value.pause()
|
||||
return
|
||||
}
|
||||
|
||||
if (currentSrc.value !== src) {
|
||||
audio.value.src = src
|
||||
currentSrc.value = src
|
||||
currentName.value = name
|
||||
}
|
||||
|
||||
audio.value.play()
|
||||
}
|
||||
|
||||
function pause() {
|
||||
audio.value?.pause()
|
||||
}
|
||||
|
||||
function seek(time: number) {
|
||||
if (audio.value) {
|
||||
audio.value.currentTime = time
|
||||
}
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (audio.value) {
|
||||
audio.value.pause()
|
||||
audio.value.currentTime = 0
|
||||
}
|
||||
playing.value = false
|
||||
currentSrc.value = null
|
||||
currentName.value = ''
|
||||
}
|
||||
|
||||
const progress = computed(() => {
|
||||
if (duration.value === 0) return 0
|
||||
return (currentTime.value / duration.value) * 100
|
||||
})
|
||||
|
||||
export function useAudioPlayer() {
|
||||
return {
|
||||
play,
|
||||
pause,
|
||||
seek,
|
||||
stop,
|
||||
playing,
|
||||
currentName,
|
||||
currentTime,
|
||||
duration,
|
||||
progress,
|
||||
currentSrc,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user