Files
indee-demo/.cursor/rules/vue-conventions.mdc
Dorian 0bb1bcc5f9 Initial commit: IndeeHub decentralized streaming platform
Built a complete Netflix-style streaming interface for IndeeHub's decentralized media platform with real film content.

Features:
- Vue 3 + TypeScript + Vite setup with hot module reloading
- Netflix-inspired UI with hero section and horizontal scrolling content rows
- Glass morphism design system with custom Tailwind configuration
- 20+ real IndeeHub films organized into 6 categories (Bitcoin, Documentaries, Drama, etc.)
- Full-featured video player component with custom controls
- Mobile-responsive design with bottom navigation
- Nostr integration ready (nostr-tools, relay pool, NIP-71 support)
- Pinia state management for content
- MCP tools configured (Filesystem, Memory, Nostr, Puppeteer)

Components:
- Browse.vue: Main streaming interface with hero and content rows
- ContentRow.vue: Horizontal scrolling film cards with navigation arrows
- VideoPlayer.vue: Custom video player with play/pause, seek, volume, fullscreen
- MobileNav.vue: Bottom tab navigation for mobile devices

Tech Stack:
- Frontend: Vue 3 (Composition API), TypeScript
- Build: Vite 7
- Styling: Tailwind CSS with custom theme
- State: Pinia 3
- Router: Vue Router 4.6
- Protocol: Nostr (nostr-tools 2.22)

Design:
- 4px grid spacing system
- Glass morphism UI components
- Netflix-style hero section with featured content
- Smooth animations and hover effects
- Mobile-first responsive breakpoints
- Dark theme with custom color palette

Content:
- 20+ IndeeHub films with titles, descriptions, categories
- Bitcoin documentaries: God Bless Bitcoin, Dirty Coin, Searching for Satoshi
- Independent films and documentaries
- Working Unsplash CDN images for thumbnails and backdrops

Ready for deployment to Umbrel, Start9, and Archy nodes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 22:19:47 +00:00

201 lines
3.9 KiB
Plaintext

---
description: Vue 3 Composition API conventions and best practices
alwaysApply: false
globs: **/*.vue
---
# Vue.js Conventions & Best Practices
## Component Architecture
### Use Composition API with `<script setup>`
```vue
<script setup>
import { ref, computed, onMounted } from 'vue'
// Props
const props = defineProps({
userId: {
type: String,
required: true
}
})
// State
const user = ref(null)
const isLoading = ref(false)
// Computed
const displayName = computed(() => {
return user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
})
// Methods
const fetchUser = async () => {
isLoading.value = true
try {
// Fetch logic
} finally {
isLoading.value = false
}
}
// Lifecycle
onMounted(() => {
fetchUser()
})
</script>
```
### Component Organization Order
1. **Imports** - External, then internal
2. **Props** - TypeScript-style validation
3. **Emits** - Explicitly defined
4. **State** (refs and reactive)
5. **Computed** - Derived values
6. **Watchers** - Side effects
7. **Methods** - Business logic
8. **Lifecycle hooks** - Ordered by execution
9. **Expose** - Public API (if needed)
## Props Best Practices
### Always Validate Props
```javascript
defineProps({
title: {
type: String,
required: true,
validator: (value) => value.length > 0
},
status: {
type: String,
default: 'pending',
validator: (value) => ['pending', 'active', 'complete'].includes(value)
}
})
```
### Prop Naming
- Use descriptive names: `isLoading` not `loading`
- Boolean props: `is`, `has`, `can`, `should`
- Avoid abbreviations: `projectData` not `projData`
## Reactive State
### When to Use ref vs reactive
**Use `ref` for:**
- Primitives (string, number, boolean)
- Single values
- When you need `.value` explicit access
**Use `reactive` for:**
- Objects with multiple properties
- Complex nested data structures
## Event Handling
### Define Emits Explicitly
```javascript
const emit = defineEmits(['update', 'delete', 'close'])
const handleUpdate = () => {
emit('update', { id: 1, name: 'Updated' })
}
```
## Template Best Practices
### Keep Templates Clean
```vue
<!-- Good - Logic in script -->
<template>
<div class="project-card" :class="cardClasses">
<h3>{{ project.title }}</h3>
<p v-if="hasDescription">{{ project.description }}</p>
</div>
</template>
<script setup>
const cardClasses = computed(() => ({
'is-featured': project.featured,
'is-complete': project.status === 'complete'
}))
const hasDescription = computed(() => {
return project.description && project.description.length > 0
})
</script>
```
## Performance Optimization
### Lazy Loading Components
```javascript
// Router lazy loading
const ProjectDetail = () => import('@/pages/ProjectDetail.vue')
// Component lazy loading
const HeavyChart = defineAsyncComponent(() =>
import('@/components/HeavyChart.vue')
)
```
### Use `v-once` for Static Content
```vue
<div v-once>
<h1>{{ staticTitle }}</h1>
</div>
```
### Shallow Reactive for Large Objects
```javascript
import { shallowRef } from 'vue'
const projects = shallowRef([...largeArray])
```
## Common Pitfalls to Avoid
### ❌ Mutating Props
Never mutate props directly - emit events instead
### ❌ Forgetting .value in script
```javascript
const count = ref(0)
if (count.value === 0) { } // ✅ Correct
```
### ❌ Creating Objects in Template
```vue
<!-- Bad - Creates new object every render -->
<Child :config="{ theme: 'dark' }" />
<!-- Good - Stable reference -->
<script setup>
const config = { theme: 'dark' }
</script>
<Child :config="config" />
```
## Summary Checklist
- ✅ Use Composition API with `<script setup>`
- ✅ Validate all props with types and defaults
- ✅ Define emits explicitly
- ✅ Keep templates clean - move logic to script
- ✅ Use computed for derived state
- ✅ Always provide `:key` for lists
- ✅ Handle errors gracefully
- ✅ Lazy load heavy components
- ✅ Never mutate props
- ✅ Remember `.value` for refs in script