UI mejorada

This commit is contained in:
2025-08-10 00:45:59 -06:00
parent 2f878a857a
commit 493d236dc4
8 changed files with 56 additions and 25 deletions

View File

@@ -201,7 +201,7 @@ onMounted(() => {
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: -1; z-index: 0; /* Render above body background but below main content */
overflow: hidden; overflow: hidden;
pointer-events: none; pointer-events: none;
} }
@@ -357,4 +357,4 @@ onMounted(() => {
will-change: transform; will-change: transform;
transform: translateZ(0); transform: translateZ(0);
} }
</style> </style>

View File

@@ -56,7 +56,15 @@ const hasActiveTrack = computed(() => !!props.currentTrack)
<style scoped> <style scoped>
.main-container { .main-container {
height: 90vh; /* Parametrized viewport sizing */
--app-vertical-margin: 10px; /* Top/bottom margin between both components */
--playback-controls-height: 60px; /* Adjustable height for PlaybackControls */
--tracklist-height: calc(100vh - (var(--app-vertical-margin) * 2) - var(--playback-controls-height));
/* Container takes full viewport minus vertical margins */
height: calc(100vh - (var(--app-vertical-margin) * 2));
margin-top: var(--app-vertical-margin);
margin-bottom: var(--app-vertical-margin);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
max-width: 1200px; max-width: 1200px;
@@ -64,7 +72,9 @@ const hasActiveTrack = computed(() => !!props.currentTrack)
padding: 20px; padding: 20px;
padding-bottom: 0; padding-bottom: 0;
position: relative; position: relative;
overflow: hidden; /* Keep shadows visible and allow backdrop to show through */
overflow: visible;
z-index: 1; /* Ensure content sits above Aurora background */
} }
.app-header { .app-header {
@@ -186,7 +196,8 @@ const hasActiveTrack = computed(() => !!props.currentTrack)
.main-container { .main-container {
padding: 15px; padding: 15px;
padding-bottom: 0; padding-bottom: 0;
height: 92vh; /* Keep same parametrized behavior on mobile */
height: calc(100vh - (var(--app-vertical-margin) * 2));
} }
.header-content { .header-content {
@@ -275,4 +286,4 @@ const hasActiveTrack = computed(() => !!props.currentTrack)
background: var(--accent-primary); background: var(--accent-primary);
} }
} }
</style> </style>

View File

@@ -65,6 +65,8 @@ const cycleRepeat = () => {
<style scoped> <style scoped>
.playback-controls { .playback-controls {
/* Allow parent to control overall height budget */
height: var(--playback-controls-height, auto);
display: flex; display: flex;
gap: 10px; gap: 10px;
align-items: center; align-items: center;
@@ -101,4 +103,4 @@ const cycleRepeat = () => {
gap: 8px; gap: 8px;
} }
} }
</style> </style>

View File

@@ -78,6 +78,8 @@ const handleTrackClick = (track, index) => {
<style scoped> <style scoped>
.track-list { .track-list {
/* Cap height so sum with PlaybackControls fits 100vh; flex handles actual sizing */
max-height: var(--tracklist-height, none);
padding: 20px; padding: 20px;
margin-bottom: 0; margin-bottom: 0;
background: rgba(255, 255, 255, 0.05); background: rgba(255, 255, 255, 0.05);
@@ -277,4 +279,4 @@ const handleTrackClick = (track, index) => {
gap: 6px; gap: 6px;
} }
} }
</style> </style>

View File

@@ -4,7 +4,7 @@ services:
container_name: repodructor container_name: repodructor
restart: unless-stopped restart: unless-stopped
volumes: volumes:
# Mount manually mounted NAS directory # Mount music directory from server
- /srv/repodructor/musica:/app/public/music:ro - /srv/repodructor/musica:/app/public/music:ro
environment: environment:
- NODE_ENV=production - NODE_ENV=production

View File

@@ -15,8 +15,8 @@ export default defineNuxtConfig({
// Server configuration for proxy compatibility // Server configuration for proxy compatibility
devServer: { devServer: {
host: '0.0.0.0', host: process.env.NUXT_HOST || '0.0.0.0',
port: 3000, port: process.env.NUXT_PORT ? Number(process.env.NUXT_PORT) : 3000,
https: false https: false
}, },
@@ -26,13 +26,12 @@ export default defineNuxtConfig({
// Vite configuration for HMR through proxy // Vite configuration for HMR through proxy
vite: { vite: {
server: { server: {
hmr: { // Configure HMR via env when developing behind HTTPS proxy
// Use proxy host instead of direct connection hmr: process.env.NODE_ENV !== 'production' ? {
host: 'musica.nucleoriofrio.com', host: process.env.HMR_HOST || undefined,
// Use default HTTPS port (443) through proxy clientPort: process.env.HMR_CLIENT_PORT ? Number(process.env.HMR_CLIENT_PORT) : undefined,
clientPort: 443, protocol: process.env.HMR_PROTOCOL || undefined
protocol: 'wss' } : undefined
}
} }
}, },
@@ -139,7 +138,7 @@ export default defineNuxtConfig({
// Runtime configuration // Runtime configuration
runtimeConfig: { runtimeConfig: {
public: { public: {
musicPath: '/music' musicPath: process.env.NUXT_PUBLIC_MUSIC_PATH || '/music'
} }
}, },
}) })

View File

@@ -16,18 +16,23 @@ export default defineEventHandler(async (event) => {
const headers = getHeaders(event) const headers = getHeaders(event)
const realIP = headers['x-real-ip'] || headers['x-forwarded-for'] || 'unknown' const realIP = headers['x-real-ip'] || headers['x-forwarded-for'] || 'unknown'
console.log(`[MUSIC API] Request from ${realIP} for file: ${filename}`) console.log(`[MUSIC API] Request from ${realIP} for file: ${filename}`)
console.log('Original filename bytes:', [...filename].map(c => c.charCodeAt(0)))
// Decode the filename // Decode the filename
try { try {
filename = decodeURIComponent(filename) filename = decodeURIComponent(filename)
console.log('Decoded filename:', filename) console.log('Decoded filename:', filename)
console.log('Decoded filename bytes:', [...filename].map(c => c.charCodeAt(0)))
} catch (error) { } catch (error) {
console.error('Error decoding filename:', error) console.error('Error decoding filename:', error)
// If decoding fails, use original filename // If decoding fails, use original filename
} }
try { try {
const musicDir = join(process.cwd(), 'public', 'music') const config = useRuntimeConfig()
const defaultPublicPath = config.public?.musicPath || '/music'
const publicRel = defaultPublicPath.replace(/^\//, '')
const musicDir = process.env.MUSIC_DIR || join(process.cwd(), 'public', publicRel)
const filePath = join(musicDir, filename) const filePath = join(musicDir, filename)
// Security check: ensure the file is within the music directory // Security check: ensure the file is within the music directory
@@ -41,7 +46,16 @@ export default defineEventHandler(async (event) => {
// Check if file exists // Check if file exists
try { try {
await fs.access(filePath) await fs.access(filePath)
} catch { console.log('File found successfully:', filePath)
} catch (error) {
console.log('File NOT found:', filePath)
console.log('Directory contents:')
try {
const files = await fs.readdir(musicDir)
files.forEach(file => console.log(' -', file))
} catch (e) {
console.log('Cannot read directory:', e)
}
throw createError({ throw createError({
statusCode: 404, statusCode: 404,
statusMessage: 'File not found' statusMessage: 'File not found'
@@ -100,4 +114,4 @@ export default defineEventHandler(async (event) => {
statusMessage: 'Failed to serve music file' statusMessage: 'Failed to serve music file'
}) })
} }
}) })

View File

@@ -8,7 +8,10 @@ export default defineEventHandler(async (event) => {
const realIP = headers['x-real-ip'] || headers['x-forwarded-for'] || 'unknown' const realIP = headers['x-real-ip'] || headers['x-forwarded-for'] || 'unknown'
console.log(`[MUSIC API] Music list request from ${realIP}`) console.log(`[MUSIC API] Music list request from ${realIP}`)
const musicDir = join(process.cwd(), 'public', 'music') const config = useRuntimeConfig()
const defaultPublicPath = config.public?.musicPath || '/music'
const publicRel = defaultPublicPath.replace(/^\//, '')
const musicDir = process.env.MUSIC_DIR || join(process.cwd(), 'public', publicRel)
// Check if music directory exists // Check if music directory exists
try { try {
@@ -54,4 +57,4 @@ export default defineEventHandler(async (event) => {
statusMessage: 'Failed to load music files' statusMessage: 'Failed to load music files'
}) })
} }
}) })