feat(chat): enhance chat functionality with song integration and UI improvements
- Updated ChatHeader and ChatMessage components to support song selection and display. - Enhanced useContentPanel to handle both film and song content, allowing for richer interactions. - Improved FilmCard and added SongCard components for better media representation. - Refactored environment variables for better configuration management. - Updated .gitignore to include development chat history. Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div class="relative flex-1 flex flex-col min-h-0 overflow-hidden">
|
||||
<div
|
||||
v-if="contextType === 'film' || contextType === 'song'"
|
||||
class="flex-1 flex flex-col min-h-0"
|
||||
>
|
||||
<div
|
||||
class="p-4 shrink-0 flex items-center justify-between"
|
||||
:style="isDark
|
||||
? 'border-bottom: 1px solid rgba(255, 255, 255, 0.08)'
|
||||
: 'border-bottom: 1px solid rgba(0, 0, 0, 0.06)'"
|
||||
>
|
||||
<p class="text-sm font-medium"
|
||||
:class="isDark ? 'text-white/70' : 'text-gray-600'">
|
||||
{{ contextLabel }}
|
||||
</p>
|
||||
<p class="text-[10px] font-mono uppercase tracking-[0.2em]"
|
||||
:class="isDark ? 'text-white/25' : 'text-gray-400'">
|
||||
Surfacing…
|
||||
</p>
|
||||
</div>
|
||||
<LoadingFilmGrid :count="12" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="relative flex-1 flex items-center justify-center min-h-0"
|
||||
>
|
||||
<div
|
||||
class="absolute inset-0 opacity-[0.04] pointer-events-none"
|
||||
:class="isDark ? 'bg-white' : 'bg-black'"
|
||||
style="background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")"
|
||||
/>
|
||||
<div class="relative flex flex-col items-center gap-8">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
v-for="(_, i) in 5"
|
||||
:key="i"
|
||||
class="w-12 h-16 rounded-lg overflow-hidden relative animate-cell-pulse"
|
||||
:class="isDark
|
||||
? 'bg-white/8 border border-white/15 shadow-xl shadow-accent/5'
|
||||
: 'bg-black/6 border border-black/8 shadow-xl shadow-accent/10'"
|
||||
:style="{ animationDelay: `${i * 100}ms` }"
|
||||
>
|
||||
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
||||
<div
|
||||
class="absolute inset-0 w-1/2 animate-shimmer-sweep"
|
||||
:class="isDark
|
||||
? 'bg-gradient-to-r from-transparent via-accent/25 to-transparent'
|
||||
: 'bg-gradient-to-r from-transparent via-accent/35 to-transparent'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm font-medium"
|
||||
:class="isDark ? 'text-white/70' : 'text-gray-600'">
|
||||
{{ contextLabel }}
|
||||
</p>
|
||||
<div class="w-40 h-px rounded-full overflow-hidden"
|
||||
:class="isDark ? 'bg-white/8' : 'bg-black/8'">
|
||||
<div class="h-full bg-accent/90 rounded-full animate-progress-sweep" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import LoadingFilmGrid from './LoadingFilmGrid.vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
contextType?: 'film' | 'song' | 'generic'
|
||||
}>(),
|
||||
{ contextType: 'film' }
|
||||
)
|
||||
|
||||
const { isDark } = useTheme()
|
||||
|
||||
const contextLabel = computed(() => {
|
||||
if (props.contextType === 'film') return 'Film recommendations'
|
||||
if (props.contextType === 'song') return 'Song recommendations'
|
||||
return 'Content'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.animate-cell-pulse {
|
||||
animation: cell-pulse 1.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||||
}
|
||||
|
||||
.animate-shimmer-sweep {
|
||||
animation: shimmer-sweep 2.2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||||
}
|
||||
|
||||
.animate-progress-sweep {
|
||||
animation: progress-sweep 1.6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes cell-pulse {
|
||||
0%, 100% { opacity: 0.6; transform: scale(0.96); }
|
||||
50% { opacity: 1; transform: scale(1.02); }
|
||||
}
|
||||
|
||||
@keyframes shimmer-sweep {
|
||||
0% { transform: translateX(-100%); }
|
||||
60% { transform: translateX(200%); }
|
||||
100% { transform: translateX(200%); }
|
||||
}
|
||||
|
||||
@keyframes progress-sweep {
|
||||
0% { width: 0; margin-left: 0; }
|
||||
45% { width: 60%; margin-left: 20%; }
|
||||
90% { width: 0; margin-left: 100%; }
|
||||
100% { width: 0; margin-left: 0; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user