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:
Dorian
2026-03-04 23:05:01 +00:00
co-authored by Claude Opus 4.6
parent 173bf8fc0f
commit d7ff678e9d
26 changed files with 2053 additions and 265 deletions
@@ -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,
}
}