feat(discover): content discovery panel with For You, tags, playlists, collections (M13.1-M13.8)

- For You feed: frequency map from favorites, sorted by most-favorited type
- Content tagging: user tags on any item, tag cloud, filter by tag
- Smart playlists: recently played, most played, by genre, by decade
- Similar content: background AI call for 3 suggestions, cached 7 days
- Recently viewed history: last 50 items with time-ago display
- Content collections: user-curated mixed-type lists with mosaic grid
- Trending: most-referenced items across 30 days with badge
- Share to Nostr: compose preview, sign via NIP-07, broadcast to relays

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 00:44:30 +00:00
co-authored by Claude Opus 4.6
parent d5d8a1f8b2
commit 52e62e33cd
9 changed files with 1023 additions and 2 deletions
@@ -45,7 +45,7 @@
: isDark
? 'text-white/40 hover:text-white/70 hover:bg-white/5'
: 'text-gray-500 hover:text-gray-800 hover:bg-black/5'"
@click="tab === 'favorites' ? (activeTab = tab) : setActiveTab(tab)"
@click="tab === 'favorites' || tab === 'discover' ? (activeTab = tab) : setActiveTab(tab)"
>
{{ tabLabel(tab) }}
</button>
@@ -168,6 +168,9 @@
<FavoritesGrid
v-else-if="activeTab === 'favorites'"
/>
<DiscoverPanel
v-else-if="activeTab === 'discover'"
/>
</ErrorBoundary>
</div>
</aside>
@@ -194,6 +197,7 @@ import MagazineGrid from './MagazineGrid.vue'
import ProjectGrid from './ProjectGrid.vue'
import NostrGrid from './NostrGrid.vue'
import FavoritesGrid from './FavoritesGrid.vue'
import DiscoverPanel from './DiscoverPanel.vue'
import ErrorBoundary from '@/components/ui/ErrorBoundary.vue'
import { useFavoritesStore } from '@/stores/favorites'
@@ -259,6 +263,9 @@ const displayTabs = computed(() => {
if (favoritesStore.items.length > 0 && !tabs.includes('favorites')) {
tabs.push('favorites')
}
if (!tabs.includes('discover')) {
tabs.push('discover')
}
return tabs
})
@@ -281,6 +288,7 @@ const TAB_LABELS: Record<ContentTab, string> = {
'design-system': 'Design',
nostr: 'Nostr',
favorites: 'Favorites',
discover: 'Discover',
}
function tabLabel(tab: ContentTab): string {