archy/neode-ui/src/components/cloud/CloudToolbar.vue
Dorian d7ff678e9d 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>
2026-03-04 23:05:01 +00:00

100 lines
3.8 KiB
Vue

<template>
<div class="cloud-toolbar">
<!-- Breadcrumbs -->
<nav class="cloud-breadcrumbs">
<button
v-for="(crumb, i) in breadcrumbs"
:key="crumb.path"
class="cloud-breadcrumb-item"
:class="{ 'cloud-breadcrumb-active': i === breadcrumbs.length - 1 }"
@click="i < breadcrumbs.length - 1 && $emit('navigate', crumb.path)"
>
<svg v-if="i === 0" class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span v-else>{{ crumb.name }}</span>
<svg v-if="i < breadcrumbs.length - 1" class="w-3 h-3 text-white/30 mx-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
</nav>
<!-- Actions -->
<div class="flex items-center gap-2">
<!-- View toggle -->
<div class="cloud-view-toggle">
<button
class="cloud-view-toggle-btn"
:class="{ 'cloud-view-toggle-active': viewMode === 'grid' }"
title="Grid view"
@click="$emit('update:viewMode', 'grid')"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zm10 0a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" />
</svg>
</button>
<button
class="cloud-view-toggle-btn"
:class="{ 'cloud-view-toggle-active': viewMode === 'list' }"
title="List view"
@click="$emit('update:viewMode', 'list')"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
</svg>
</button>
</div>
<button class="glass-button cloud-toolbar-btn" title="Upload file" @click="triggerUpload">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
</svg>
<span class="hidden md:inline">Upload</span>
</button>
<button class="glass-button cloud-toolbar-btn" title="Refresh" @click="$emit('refresh')">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</button>
</div>
<input
ref="fileInput"
type="file"
class="hidden"
multiple
@change="handleFileSelect"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{
breadcrumbs: { name: string; path: string }[]
viewMode: 'list' | 'grid'
}>()
const emit = defineEmits<{
navigate: [path: string]
refresh: []
upload: [files: File[]]
'update:viewMode': [mode: 'list' | 'grid']
}>()
const fileInput = ref<HTMLInputElement | null>(null)
function triggerUpload() {
fileInput.value?.click()
}
function handleFileSelect(e: Event) {
const input = e.target as HTMLInputElement
if (input.files && input.files.length > 0) {
emit('upload', Array.from(input.files))
input.value = ''
}
}
</script>