{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "bookmark-manager-grid",
  "title": "Bookmark Manager Grid",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/bookmark-manager-grid/BookmarkManagerGrid.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed, ref } from 'vue'\nimport {\n  Bookmark,\n  BookOpen,\n  Check,\n  CheckCircle2,\n  Copy,\n  ExternalLink,\n  Globe,\n  Layers,\n  Palette,\n  Plus,\n  Search,\n  Bot,\n  ShieldCheck,\n  Star,\n  Tag,\n  Type,\n  X,\n  Zap,\n} from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog'\nimport { Input } from '@/components/ui/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { Separator } from '@/components/ui/separator'\n\nexport interface BookmarkItem {\n  id: string\n  title: string\n  url: string\n  domain: string\n  faviconUrl: string\n  previewImage: string\n  excerpt: string\n  collection: 'design-systems' | 'web-perf' | 'typography' | 'ai-tools' | 'security'\n  tags: string[]\n  isFavorite: boolean\n  savedAt: string\n}\n\nexport interface CollectionMeta {\n  id: string\n  name: string\n  count: number\n  iconName: 'bookmark' | 'palette' | 'zap' | 'type' | 'bot' | 'shield' | 'star'\n}\n\nconst props = defineProps<{\n  class?: HTMLAttributes['class']\n}>()\n\nconst INITIAL_BOOKMARKS: BookmarkItem[] = [\n  {\n    id: 'bm-01',\n    title: 'OKLCH Color Picker & Converter',\n    url: 'https://oklch.com',\n    domain: 'oklch.com',\n    faviconUrl: 'https://images.unsplash.com/photo-1550684848-fac1c5b4e853?w=64&auto=format&fit=crop&q=80',\n    previewImage: 'https://images.unsplash.com/photo-1541701494587-cb58502866ab?w=800&auto=format&fit=crop&q=80',\n    excerpt:\n      'Interactive color space playground with gamut-mapping, APCA contrast ratios, and direct CSS/Tailwind OKLCH code generation.',\n    collection: 'design-systems',\n    tags: ['#oklch', '#tailwind', '#design-systems'],\n    isFavorite: true,\n    savedAt: 'Aug 18, 2026',\n  },\n  {\n    id: 'bm-02',\n    title: 'Reka UI Headless Vue Primitives',\n    url: 'https://reka-ui.com',\n    domain: 'reka-ui.com',\n    faviconUrl: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=64&auto=format&fit=crop&q=80',\n    previewImage: 'https://images.unsplash.com/photo-1507238691740-187a5b1d37b8?w=800&auto=format&fit=crop&q=80',\n    excerpt:\n      'Accessible, completely unstyled UI component primitives for Vue 3 with WAI-ARIA compliant keyboard focus and composite slots.',\n    collection: 'design-systems',\n    tags: ['#reka-ui', '#vue', '#design-systems'],\n    isFavorite: true,\n    savedAt: 'Aug 15, 2026',\n  },\n  {\n    id: 'bm-03',\n    title: 'Web Vitals Performance Playbook',\n    url: 'https://web.dev/vitals',\n    domain: 'web.dev',\n    faviconUrl: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=64&auto=format&fit=crop&q=80',\n    previewImage: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800&auto=format&fit=crop&q=80',\n    excerpt:\n      'Diagnostic field guides for optimizing Interaction to Next Paint (INP), Largest Contentful Paint (LCP), and layout stability.',\n    collection: 'web-perf',\n    tags: ['#web-vitals', '#web-perf', '#astro'],\n    isFavorite: false,\n    savedAt: 'Aug 12, 2026',\n  },\n  {\n    id: 'bm-04',\n    title: 'Typewolf Font Trends & Pairings',\n    url: 'https://typewolf.com',\n    domain: 'typewolf.com',\n    faviconUrl: 'https://images.unsplash.com/photo-1455390582262-044cdead277a?w=64&auto=format&fit=crop&q=80',\n    previewImage: 'https://images.unsplash.com/photo-1516962215378-7fa2e137ae93?w=800&auto=format&fit=crop&q=80',\n    excerpt:\n      'The definitive guide to web typography, font pairings, lookbooks, variable font loading strategies, and typographic hierarchy.',\n    collection: 'typography',\n    tags: ['#typography', '#design-systems', '#react'],\n    isFavorite: true,\n    savedAt: 'Aug 09, 2026',\n  },\n  {\n    id: 'bm-05',\n    title: 'Vercel AI SDK 3.0 Documentation',\n    url: 'https://sdk.vercel.ai/docs',\n    domain: 'sdk.vercel.ai',\n    faviconUrl: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=64&auto=format&fit=crop&q=80',\n    previewImage: 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800&auto=format&fit=crop&q=80',\n    excerpt:\n      'Universal TypeScript library for streaming chat completions, structured JSON generation, tool calling, and multi-model routing.',\n    collection: 'ai-tools',\n    tags: ['#ai-prompt', '#react', '#astro'],\n    isFavorite: false,\n    savedAt: 'Aug 06, 2026',\n  },\n  {\n    id: 'bm-06',\n    title: 'OWASP API Security Top 10 Guide',\n    url: 'https://owasp.org/API-Security',\n    domain: 'owasp.org',\n    faviconUrl: 'https://images.unsplash.com/photo-1563986768609-322da13575f3?w=64&auto=format&fit=crop&q=80',\n    previewImage: 'https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?w=800&auto=format&fit=crop&q=80',\n    excerpt:\n      'Crucial security vectors for modern APIs including broken object authorization, rate limiting flaws, and JWT key hygiene.',\n    collection: 'security',\n    tags: ['#security', '#web-perf'],\n    isFavorite: false,\n    savedAt: 'Aug 03, 2026',\n  },\n]\n\nconst COLLECTIONS: CollectionMeta[] = [\n  { id: 'all', name: 'All Bookmarks', count: 142, iconName: 'bookmark' },\n  { id: 'design-systems', name: 'Design Systems', count: 28, iconName: 'palette' },\n  { id: 'web-perf', name: 'Web Performance', count: 19, iconName: 'zap' },\n  { id: 'typography', name: 'Typography & Fonts', count: 14, iconName: 'type' },\n  { id: 'ai-tools', name: 'AI & LLM Tools', count: 35, iconName: 'bot' },\n  { id: 'security', name: 'Security & Auth', count: 22, iconName: 'shield' },\n  { id: 'favorites', name: 'Favorites ★', count: 18, iconName: 'star' },\n]\n\nconst TAG_CLOUD = [\n  '#reka-ui',\n  '#tailwind',\n  '#oklch',\n  '#astro',\n  '#react',\n  '#vue',\n  '#typography',\n  '#web-vitals',\n  '#security',\n  '#ai-prompt',\n]\n\nconst bookmarks = ref<BookmarkItem[]>([...INITIAL_BOOKMARKS])\nconst searchQuery = ref('')\nconst selectedCollection = ref('all')\nconst selectedTag = ref<string | null>(null)\n\n// Add Bookmark Modal state\nconst isAddModalOpen = ref(false)\nconst formUrl = ref('')\nconst formTitle = ref('')\nconst formDomain = ref('')\nconst formExcerpt = ref('')\nconst formCollection = ref<'design-systems' | 'web-perf' | 'typography' | 'ai-tools' | 'security'>('design-systems')\nconst formTags = ref('')\nconst formPreview = ref('')\n\n// Toast Notification\nconst notificationToast = ref<string | null>(null)\nlet toastTimeout: ReturnType<typeof setTimeout> | null = null\n\nfunction showNotification(msg: string) {\n  if (toastTimeout) clearTimeout(toastTimeout)\n  notificationToast.value = msg\n  toastTimeout = setTimeout(() => {\n    notificationToast.value = null\n  }, 2800)\n}\n\n// Copy URL feedback\nconst copiedId = ref<string | null>(null)\nlet copyTimeout: ReturnType<typeof setTimeout> | null = null\n\nfunction copyUrl(item: BookmarkItem, event?: MouseEvent) {\n  if (event) event.stopPropagation()\n  navigator.clipboard?.writeText(item.url)\n  copiedId.value = item.id\n  showNotification(`Copied link to clipboard: ${item.domain}`)\n  if (copyTimeout) clearTimeout(copyTimeout)\n  copyTimeout = setTimeout(() => {\n    copiedId.value = null\n  }, 2000)\n}\n\n// Toggle Favorite status\nfunction toggleFavorite(item: BookmarkItem, event?: MouseEvent) {\n  if (event) event.stopPropagation()\n  item.isFavorite = !item.isFavorite\n  if (item.isFavorite) {\n    showNotification(`Starred \"${item.title}\"`)\n  } else {\n    showNotification(`Removed \"${item.title}\" from favorites`)\n  }\n}\n\n// Toggle Tag Filter\nfunction toggleTag(tag: string) {\n  if (selectedTag.value === tag) {\n    selectedTag.value = null\n  } else {\n    selectedTag.value = tag\n  }\n}\n\n// Reset all active filters\nfunction resetFilters() {\n  searchQuery.value = ''\n  selectedCollection.value = 'all'\n  selectedTag.value = null\n}\n\nfunction setPresetUrl(url: string) {\n  formUrl.value = url\n  autoFetchMetadata()\n}\n\n// Auto-fetch / simulate metadata fetching for Add Bookmark modal\nfunction autoFetchMetadata() {\n  const url = formUrl.value.trim()\n  if (!url) {\n    showNotification('Please enter a URL first')\n    return\n  }\n\n  let extractedDomain = ''\n  try {\n    const parsed = new URL(url.startsWith('http') ? url : `https://${url}`)\n    extractedDomain = parsed.hostname.replace(/^www\\./, '')\n  } catch {\n    extractedDomain = 'developer.mozilla.org'\n  }\n\n  formDomain.value = extractedDomain\n\n  if (url.includes('github') || extractedDomain.includes('github')) {\n    formTitle.value = 'GitHub Engineering Architecture Guides'\n    formExcerpt.value = 'In-depth architectural post-mortems, system scaling principles, and resilient CI/CD pipelines.'\n    formCollection.value = 'web-perf'\n    formTags.value = '#web-perf, #security'\n    formPreview.value = 'https://images.unsplash.com/photo-1618401471353-b98afee0b2eb?w=800&auto=format&fit=crop&q=80'\n  } else if (url.includes('astro') || extractedDomain.includes('astro')) {\n    formTitle.value = 'Astro 4.0 Server Islands & SSG Docs'\n    formExcerpt.value =\n      'Zero-JS by default frontend framework combining island architecture with fast static HTML builds.'\n    formCollection.value = 'design-systems'\n    formTags.value = '#astro, #react, #vue'\n    formPreview.value = 'https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800&auto=format&fit=crop&q=80'\n  } else if (url.includes('linear') || extractedDomain.includes('linear')) {\n    formTitle.value = 'Linear Method — Principles for Modern Software'\n    formExcerpt.value =\n      'Practices and philosophies for high-craft product execution, issue triage, and keyboard-first UI.'\n    formCollection.value = 'design-systems'\n    formTags.value = '#design-systems, #tailwind'\n    formPreview.value = 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&auto=format&fit=crop&q=80'\n  } else {\n    formTitle.value = formTitle.value || `${extractedDomain.toUpperCase()} — Resource Guide`\n    formExcerpt.value =\n      formExcerpt.value || 'Curated documentation and developer tooling reference for modern engineering workflows.'\n    formTags.value = formTags.value || '#oklch, #tailwind'\n    formPreview.value =\n      formPreview.value ||\n      'https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?w=800&auto=format&fit=crop&q=80'\n  }\n\n  showNotification(`Fetched metadata from ${extractedDomain}`)\n}\n\nfunction handleSaveBookmark() {\n  if (!formUrl.value.trim()) {\n    showNotification('Please enter a valid URL')\n    return\n  }\n\n  const finalTitle = formTitle.value.trim() || formDomain.value.trim() || 'Untitled Bookmark'\n  let cleanDomain = formDomain.value.trim()\n  if (!cleanDomain) {\n    try {\n      const parsed = new URL(formUrl.value.startsWith('http') ? formUrl.value : `https://${formUrl.value}`)\n      cleanDomain = parsed.hostname.replace(/^www\\./, '')\n    } catch {\n      cleanDomain = 'web.dev'\n    }\n  }\n\n  const tagList = formTags.value\n    ? formTags.value\n        .split(',')\n        .map((t) => t.trim())\n        .filter(Boolean)\n        .map((t) => (t.startsWith('#') ? t : `#${t}`))\n    : ['#design-systems']\n\n  const newBookmark: BookmarkItem = {\n    id: `bm-${Date.now()}`,\n    title: finalTitle,\n    url: formUrl.value.startsWith('http') ? formUrl.value : `https://${formUrl.value}`,\n    domain: cleanDomain,\n    faviconUrl: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=64&auto=format&fit=crop&q=80',\n    previewImage:\n      formPreview.value ||\n      'https://images.unsplash.com/photo-1541701494587-cb58502866ab?w=800&auto=format&fit=crop&q=80',\n    excerpt: formExcerpt.value.trim() || 'Developer documentation, components, and reference guides.',\n    collection: formCollection.value,\n    tags: tagList,\n    isFavorite: false,\n    savedAt: 'Just now',\n  }\n\n  bookmarks.value.unshift(newBookmark)\n  isAddModalOpen.value = false\n\n  // Reset form\n  formUrl.value = ''\n  formTitle.value = ''\n  formDomain.value = ''\n  formExcerpt.value = ''\n  formTags.value = ''\n  formPreview.value = ''\n\n  showNotification(`Saved \"${finalTitle}\" to bookmarks!`)\n}\n\n// Filtered bookmarks computed pipeline\nconst filteredBookmarks = computed(() => {\n  return bookmarks.value.filter((item) => {\n    // Collection match\n    if (selectedCollection.value === 'favorites') {\n      if (!item.isFavorite) return false\n    } else if (selectedCollection.value !== 'all') {\n      if (item.collection !== selectedCollection.value) return false\n    }\n\n    // Tag match\n    if (selectedTag.value && !item.tags.includes(selectedTag.value)) {\n      return false\n    }\n\n    // Search query match\n    if (searchQuery.value.trim() !== '') {\n      const q = searchQuery.value.toLowerCase().trim()\n      const titleMatch = item.title.toLowerCase().includes(q)\n      const domainMatch = item.domain.toLowerCase().includes(q)\n      const excerptMatch = item.excerpt.toLowerCase().includes(q)\n      const urlMatch = item.url.toLowerCase().includes(q)\n      const tagMatch = item.tags.some((t) => t.toLowerCase().includes(q))\n      if (!titleMatch && !domainMatch && !excerptMatch && !urlMatch && !tagMatch) {\n        return false\n      }\n    }\n\n    return true\n  })\n})\n\nconst activeCollectionMeta = computed(() => {\n  return COLLECTIONS.find((c) => c.id === selectedCollection.value) || COLLECTIONS[0]\n})\n</script>\n\n<template>\n  <div data-slot=\"bookmark-manager-grid\" :class=\"cn('w-full space-y-6', props.class)\">\n    <!-- Top Floating Toast Notification -->\n    <div\n      v-if=\"notificationToast\"\n      role=\"status\"\n      aria-live=\"polite\"\n      class=\"bg-foreground text-background animate-in fade-in slide-in-from-top-2 fixed top-6 right-6 z-50 flex items-center gap-2.5 rounded-lg px-4 py-2.5 text-xs font-medium shadow-lg transition-colors\"\n    >\n      <CheckCircle2 class=\"text-primary size-4 shrink-0\" />\n      <span>{{ notificationToast }}</span>\n    </div>\n\n    <!-- Header Section -->\n    <div\n      class=\"bg-card text-card-foreground border-border/80 flex flex-col gap-5 rounded-2xl border p-5 shadow-xs sm:flex-row sm:items-center sm:justify-between lg:p-6\"\n    >\n      <div class=\"space-y-1.5\">\n        <div class=\"flex flex-wrap items-center gap-2.5\">\n          <div class=\"bg-primary/10 text-primary grid size-9 place-items-center rounded-xl shadow-xs\">\n            <Bookmark class=\"size-5\" />\n          </div>\n          <h1 class=\"text-xl font-bold tracking-tight sm:text-2xl\">Bookmarks & Engineering Resources</h1>\n          <Badge variant=\"secondary\" class=\"font-mono text-xs\"> v2.1 </Badge>\n        </div>\n        <p class=\"text-muted-foreground flex flex-wrap items-center gap-2 text-xs\">\n          <Layers class=\"size-3.5\" />\n          <span class=\"text-foreground font-medium\">142 bookmarks · 8 collections</span>\n          <span class=\"text-border hidden sm:inline\">|</span>\n          <span class=\"text-muted-foreground hidden sm:inline\">Raindrop Sync Active</span>\n          <span class=\"bg-success inline-block size-1.5 rounded-full\" />\n        </p>\n      </div>\n\n      <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n        <!-- Search Input -->\n        <div class=\"relative w-full sm:w-64 lg:w-72\">\n          <Search\n            aria-hidden=\"true\"\n            class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2\"\n          />\n          <Input\n            v-model=\"searchQuery\"\n            type=\"search\"\n            placeholder=\"Search bookmarks, domains, tags…\"\n            class=\"h-9 pr-8 pl-9 text-xs\"\n          />\n          <button\n            v-if=\"searchQuery\"\n            type=\"button\"\n            class=\"text-muted-foreground hover:text-foreground absolute top-1/2 right-2.5 min-h-6 -translate-y-1/2 text-xs\"\n            aria-label=\"Clear search\"\n            @click=\"searchQuery = ''\"\n          >\n            <X class=\"size-3.5\" />\n          </button>\n        </div>\n\n        <!-- Add Bookmark Button -->\n        <Button size=\"sm\" class=\"shrink-0 font-medium active:scale-[0.98]\" @click=\"isAddModalOpen = true\">\n          <Plus class=\"size-4\" />\n          Add Bookmark\n        </Button>\n      </div>\n    </div>\n\n    <!-- Mobile Horizontal Collections Bar (Visible on mobile/tablet) -->\n    <div\n      class=\"bg-card text-card-foreground border-border/80 flex items-center gap-1.5 overflow-x-auto rounded-xl border p-2 shadow-xs lg:hidden\"\n    >\n      <button\n        v-for=\"col in COLLECTIONS\"\n        :key=\"col.id\"\n        type=\"button\"\n        :class=\"\n          cn(\n            'focus-visible:ring-ring flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium whitespace-nowrap transition-colors focus-visible:ring-2 focus-visible:outline-none',\n            selectedCollection === col.id\n              ? 'bg-primary text-primary-foreground shadow-xs'\n              : 'bg-muted/60 text-muted-foreground hover:bg-muted hover:text-foreground',\n          )\n        \"\n        @click=\"selectedCollection = col.id\"\n      >\n        <span>{{ col.name }}</span>\n        <span\n          :class=\"\n            cn(\n              'py-0.2 rounded-full px-1.5 font-mono text-xs tabular-nums',\n              selectedCollection === col.id\n                ? 'bg-primary-foreground/20 text-primary-foreground'\n                : 'bg-background/80 text-muted-foreground',\n            )\n          \"\n        >\n          {{ col.count }}\n        </span>\n      </button>\n    </div>\n\n    <!-- 2-Column Main Layout (Left: Sidebar w-60; Right: Grid) -->\n    <div class=\"flex flex-col gap-6 lg:flex-row lg:items-start\">\n      <!-- Left Collections Sidebar -->\n      <aside class=\"hidden w-60 shrink-0 space-y-6 lg:block\" aria-label=\"Collections and Tags\">\n        <!-- Collections List Card -->\n        <div class=\"bg-card text-card-foreground border-border/80 space-y-3 rounded-2xl border p-4 shadow-xs\">\n          <div class=\"flex items-center justify-between px-1\">\n            <h2 class=\"text-foreground text-xs font-semibold tracking-wider uppercase\">Collections</h2>\n            <span class=\"text-muted-foreground font-mono text-xs tabular-nums\">8 folders</span>\n          </div>\n\n          <nav class=\"space-y-1\" aria-label=\"Collection folders\">\n            <button\n              v-for=\"col in COLLECTIONS\"\n              :key=\"col.id\"\n              type=\"button\"\n              :class=\"\n                cn(\n                  'focus-visible:ring-ring flex w-full items-center justify-between rounded-lg px-3 py-2 text-xs font-medium transition-[color,background-color,box-shadow,transform] duration-150 focus-visible:ring-2 focus-visible:outline-none active:scale-[0.98]',\n                  selectedCollection === col.id\n                    ? 'bg-primary/10 text-primary border-primary/20 border font-semibold shadow-xs'\n                    : 'text-muted-foreground hover:bg-muted/70 hover:text-foreground border border-transparent',\n                )\n              \"\n              @click=\"selectedCollection = col.id\"\n            >\n              <div class=\"flex items-center gap-2.5 truncate\">\n                <Bookmark v-if=\"col.iconName === 'bookmark'\" class=\"size-4 shrink-0\" />\n                <Palette v-else-if=\"col.iconName === 'palette'\" class=\"text-chart-1 size-4 shrink-0\" />\n                <Zap v-else-if=\"col.iconName === 'zap'\" class=\"text-warning size-4 shrink-0\" />\n                <Type v-else-if=\"col.iconName === 'type'\" class=\"text-info size-4 shrink-0\" />\n                <Bot v-else-if=\"col.iconName === 'bot'\" class=\"text-success size-4 shrink-0\" />\n                <ShieldCheck v-else-if=\"col.iconName === 'shield'\" class=\"text-destructive size-4 shrink-0\" />\n                <Star v-else-if=\"col.iconName === 'star'\" class=\"fill-warning text-warning size-4 shrink-0\" />\n                <span class=\"truncate\">{{ col.name }}</span>\n              </div>\n              <span\n                :class=\"\n                  cn(\n                    'rounded-md px-1.5 py-0.5 font-mono text-xs tabular-nums',\n                    selectedCollection === col.id\n                      ? 'bg-primary/20 text-primary font-bold'\n                      : 'bg-muted/60 text-muted-foreground',\n                  )\n                \"\n              >\n                {{ col.count }}\n              </span>\n            </button>\n          </nav>\n        </div>\n\n        <!-- Tags Cloud Card -->\n        <div class=\"bg-card text-card-foreground border-border/80 space-y-3 rounded-2xl border p-4 shadow-xs\">\n          <div class=\"flex items-center justify-between px-1\">\n            <h2 class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold tracking-wider uppercase\">\n              <Tag class=\"text-primary size-3.5\" />\n              Tag Cloud\n            </h2>\n            <button\n              v-if=\"selectedTag\"\n              type=\"button\"\n              class=\"text-primary min-h-6 text-xs transition-transform duration-150 hover:underline active:scale-95\"\n              @click=\"selectedTag = null\"\n            >\n              Reset\n            </button>\n          </div>\n\n          <div class=\"flex flex-wrap gap-1.5\">\n            <button\n              v-for=\"tag in TAG_CLOUD\"\n              :key=\"tag\"\n              type=\"button\"\n              :class=\"\n                cn(\n                  'focus-visible:ring-ring min-h-6 rounded-md border px-2 py-0.5 text-xs font-medium transition-[colors,transform] duration-150 focus-visible:ring-2 focus-visible:outline-none active:scale-95',\n                  selectedTag === tag\n                    ? 'border-primary bg-primary/10 text-primary font-semibold shadow-xs'\n                    : 'border-border bg-background text-muted-foreground hover:border-foreground/30 hover:text-foreground',\n                )\n              \"\n              @click=\"toggleTag(tag)\"\n            >\n              {{ tag }}\n            </button>\n          </div>\n        </div>\n\n        <!-- Quick Storage Vault Info Box -->\n        <div class=\"bg-muted/40 border-border/70 text-card-foreground space-y-2 rounded-2xl border p-4 text-xs\">\n          <div class=\"flex items-center gap-2\">\n            <div class=\"bg-primary/10 text-primary grid size-6 place-items-center rounded-md\">\n              <Globe class=\"size-3.5\" />\n            </div>\n            <span class=\"text-foreground font-semibold\">Raindrop Cloud Vault</span>\n          </div>\n          <p class=\"text-muted-foreground text-xs leading-relaxed\">\n            Encrypted cross-device bookmark vault with automated favicon caching and full-text metadata indexing.\n          </p>\n        </div>\n      </aside>\n\n      <!-- Right Bookmarks Main Area -->\n      <main class=\"min-w-0 flex-1 space-y-4\">\n        <!-- Result Bar & Filter Indicators -->\n        <div\n          class=\"bg-card text-card-foreground border-border/80 flex flex-wrap items-center justify-between gap-3 rounded-xl border px-4 py-3 shadow-xs\"\n        >\n          <div class=\"flex flex-wrap items-center gap-2 text-xs\">\n            <span class=\"text-muted-foreground\">Collection:</span>\n            <Badge variant=\"outline\" class=\"border-primary/40 bg-primary/5 text-primary text-xs font-medium\">\n              {{ activeCollectionMeta.name }}\n            </Badge>\n\n            <template v-if=\"selectedTag\">\n              <span class=\"text-muted-foreground ml-1\">Tag:</span>\n              <Badge variant=\"secondary\" class=\"text-xs font-medium\">\n                {{ selectedTag }}\n                <button\n                  type=\"button\"\n                  class=\"text-muted-foreground hover:text-foreground ml-1 inline-flex items-center\"\n                  aria-label=\"Remove tag filter\"\n                  @click=\"selectedTag = null\"\n                >\n                  <X class=\"size-3\" />\n                </button>\n              </Badge>\n            </template>\n\n            <template v-if=\"searchQuery\">\n              <span class=\"text-muted-foreground ml-1\">Query:</span>\n              <span class=\"text-foreground font-mono font-medium\">&ldquo;{{ searchQuery }}&rdquo;</span>\n            </template>\n\n            <button\n              v-if=\"selectedCollection !== 'all' || selectedTag || searchQuery\"\n              type=\"button\"\n              class=\"text-muted-foreground hover:text-foreground ml-2 min-h-6 text-xs underline underline-offset-2\"\n              @click=\"resetFilters\"\n            >\n              Clear filters\n            </button>\n          </div>\n\n          <div class=\"text-muted-foreground text-xs tabular-nums\">\n            Showing <span class=\"text-foreground font-semibold\">{{ filteredBookmarks.length }}</span> of\n            {{ bookmarks.length }} items\n          </div>\n        </div>\n\n        <!-- Bookmarks Grid (2 or 3-column responsive) -->\n        <div v-if=\"filteredBookmarks.length > 0\" class=\"grid grid-cols-1 gap-4 sm:gap-5 md:grid-cols-2 xl:grid-cols-3\">\n          <Card\n            v-for=\"item in filteredBookmarks\"\n            :key=\"item.id\"\n            class=\"group border-border/80 bg-card hover:border-border relative flex flex-col overflow-hidden rounded-xl border p-0 shadow-xs transition-colors duration-200 hover:shadow-md\"\n          >\n            <!-- Thumbnail Visual Preview -->\n            <div class=\"bg-muted/40 border-border/60 relative aspect-[16/10] w-full overflow-hidden border-b\">\n              <img\n                :src=\"item.previewImage\"\n                :alt=\"item.title\"\n                loading=\"lazy\"\n                class=\"size-full object-cover transition-transform duration-300 group-hover:scale-105\"\n              />\n\n              <!-- Gradient overlay -->\n              <div\n                class=\"absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-black/20 opacity-40 transition-opacity group-hover:opacity-60\"\n              />\n\n              <!-- Domain Badge Top Left -->\n              <div class=\"absolute top-2.5 left-2.5 flex items-center gap-1.5\">\n                <div\n                  class=\"bg-background text-foreground border-border/80 flex items-center gap-1.5 rounded-md border px-2 py-0.5 text-xs font-medium shadow-xs\"\n                >\n                  <Globe class=\"text-muted-foreground size-3\" />\n                  <span class=\"font-mono text-xs\">{{ item.domain }}</span>\n                </div>\n              </div>\n\n              <!-- Favorite Toggle Star Button Top Right -->\n              <button\n                type=\"button\"\n                :class=\"\n                  cn(\n                    'bg-background border-border/80 focus-visible:ring-ring absolute top-2.5 right-2.5 grid size-7 place-items-center rounded-full border shadow-xs transition-transform duration-150 hover:scale-105 focus-visible:ring-2 focus-visible:outline-none active:scale-90',\n                    item.isFavorite ? 'text-warning' : 'text-muted-foreground hover:text-warning',\n                  )\n                \"\n                :title=\"item.isFavorite ? 'Remove from favorites' : 'Add to favorites'\"\n                :aria-label=\"item.isFavorite ? 'Remove from favorites' : 'Add to favorites'\"\n                @click=\"(e: MouseEvent) => toggleFavorite(item, e)\"\n              >\n                <Star :class=\"cn('size-4', item.isFavorite ? 'fill-warning text-warning' : '')\" />\n              </button>\n            </div>\n\n            <!-- Card Content Body -->\n            <CardContent class=\"flex flex-1 flex-col justify-between gap-3 p-4\">\n              <div class=\"space-y-2\">\n                <div class=\"flex items-start justify-between gap-2\">\n                  <a\n                    :href=\"item.url\"\n                    target=\"_blank\"\n                    rel=\"noreferrer noopener\"\n                    class=\"group-hover:text-primary line-clamp-1 min-h-6 py-0.5 text-sm font-semibold tracking-tight transition-colors hover:underline\"\n                  >\n                    {{ item.title }}\n                  </a>\n                </div>\n\n                <p class=\"text-muted-foreground line-clamp-2 text-xs leading-relaxed\">\n                  {{ item.excerpt }}\n                </p>\n              </div>\n\n              <!-- Tag Pills -->\n              <div class=\"flex flex-wrap items-center gap-1.5\">\n                <button\n                  v-for=\"t in item.tags\"\n                  :key=\"t\"\n                  type=\"button\"\n                  :class=\"\n                    cn(\n                      'min-h-6 rounded-md px-2 py-0.5 text-xs font-medium transition-[colors,transform] duration-150 active:scale-95',\n                      selectedTag === t\n                        ? 'bg-primary text-primary-foreground font-semibold'\n                        : 'bg-muted/70 text-muted-foreground hover:bg-muted hover:text-foreground',\n                    )\n                  \"\n                  @click=\"toggleTag(t)\"\n                >\n                  {{ t }}\n                </button>\n              </div>\n\n              <!-- Card Footer Actions Row -->\n              <div class=\"border-border/60 flex items-center justify-between border-t pt-3 text-xs\">\n                <span class=\"text-muted-foreground font-mono text-xs tabular-nums\">\n                  {{ item.savedAt }}\n                </span>\n\n                <div class=\"flex items-center gap-1.5\">\n                  <Button\n                    variant=\"outline\"\n                    size=\"icon-xs\"\n                    class=\"size-7 transition-transform duration-150 active:scale-95\"\n                    :title=\"copiedId === item.id ? 'Copied URL!' : 'Copy URL'\"\n                    :aria-label=\"copiedId === item.id ? 'Copied URL' : 'Copy URL'\"\n                    @click=\"(e: MouseEvent) => copyUrl(item, e)\"\n                  >\n                    <Check v-if=\"copiedId === item.id\" class=\"text-primary size-3.5\" />\n                    <Copy v-else class=\"size-3.5\" />\n                  </Button>\n\n                  <Button\n                    variant=\"secondary\"\n                    size=\"sm\"\n                    class=\"h-7 gap-1 px-2.5 text-xs font-medium active:scale-[0.98]\"\n                    as=\"a\"\n                    :href=\"item.url\"\n                    target=\"_blank\"\n                    rel=\"noreferrer noopener\"\n                  >\n                    <span>Open</span>\n                    <ExternalLink class=\"size-3\" />\n                  </Button>\n                </div>\n              </div>\n            </CardContent>\n          </Card>\n        </div>\n\n        <!-- Empty State -->\n        <div\n          v-else\n          class=\"bg-card text-card-foreground border-border/80 flex flex-col items-center justify-center rounded-2xl border p-12 text-center shadow-xs\"\n        >\n          <div class=\"bg-muted/70 text-muted-foreground mb-3 grid size-12 place-items-center rounded-xl\">\n            <BookOpen class=\"size-6\" />\n          </div>\n          <h2 class=\"text-base font-semibold\">No bookmarks found</h2>\n          <p class=\"text-muted-foreground mt-1 max-w-sm text-xs\">\n            No engineering resources match your active collection, tag filter, or search query. Try clearing filters or\n            add a new bookmark.\n          </p>\n          <div class=\"mt-4 flex items-center gap-2\">\n            <Button variant=\"outline\" size=\"sm\" class=\"text-xs\" @click=\"resetFilters\"> Clear All Filters </Button>\n            <Button size=\"sm\" class=\"text-xs\" @click=\"isAddModalOpen = true\">\n              <Plus class=\"size-3.5\" />\n              Add Bookmark\n            </Button>\n          </div>\n        </div>\n      </main>\n    </div>\n\n    <!-- Add Bookmark Modal Dialog -->\n    <Dialog v-model:open=\"isAddModalOpen\">\n      <DialogContent class=\"sm:max-w-md\">\n        <DialogHeader class=\"text-left\">\n          <div class=\"flex items-center gap-2\">\n            <div class=\"bg-primary/10 text-primary grid size-7 place-items-center rounded-lg\">\n              <Bookmark class=\"size-4\" />\n            </div>\n            <DialogTitle class=\"text-base font-bold\">Add New Bookmark</DialogTitle>\n          </div>\n          <DialogDescription class=\"text-xs\">\n            Save a web resource or engineering tool with automated metadata fetching.\n          </DialogDescription>\n        </DialogHeader>\n\n        <form class=\"space-y-4 pt-1\" @submit.prevent=\"handleSaveBookmark\">\n          <!-- URL Input with Auto-Fetch Button -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Website URL</label>\n            <div class=\"flex items-center gap-2\">\n              <Input\n                v-model=\"formUrl\"\n                type=\"url\"\n                placeholder=\"https://astro.build or https://linear.app\"\n                class=\"h-9 text-xs\"\n                required\n              />\n              <Button\n                type=\"button\"\n                variant=\"outline\"\n                size=\"sm\"\n                class=\"h-9 shrink-0 gap-1 text-xs font-medium\"\n                title=\"Fetch metadata from URL\"\n                @click=\"autoFetchMetadata\"\n              >\n                <Globe class=\"text-primary size-3.5\" />\n                <span>Fetch</span>\n              </Button>\n            </div>\n          </div>\n\n          <!-- Quick Samples Preset Bar -->\n          <div class=\"flex flex-wrap items-center gap-1.5 text-xs\">\n            <span class=\"text-muted-foreground text-xs\">Try sample:</span>\n            <button\n              type=\"button\"\n              class=\"border-border bg-muted/60 hover:bg-muted text-muted-foreground hover:text-foreground min-h-6 rounded-md border px-2 py-0.5 text-xs transition-colors\"\n              @click=\"setPresetUrl('https://astro.build')\"\n            >\n              Astro SSG\n            </button>\n            <button\n              type=\"button\"\n              class=\"border-border bg-muted/60 hover:bg-muted text-muted-foreground hover:text-foreground min-h-6 rounded-md border px-2 py-0.5 text-xs transition-colors\"\n              @click=\"setPresetUrl('https://linear.app/method')\"\n            >\n              Linear Method\n            </button>\n            <button\n              type=\"button\"\n              class=\"border-border bg-muted/60 hover:bg-muted text-muted-foreground hover:text-foreground min-h-6 rounded-md border px-2 py-0.5 text-xs transition-colors\"\n              @click=\"setPresetUrl('https://github.com/features')\"\n            >\n              GitHub Guides\n            </button>\n          </div>\n\n          <Separator class=\"bg-border/60\" />\n\n          <!-- Title Input -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Bookmark Title</label>\n            <Input\n              v-model=\"formTitle\"\n              type=\"text\"\n              placeholder=\"e.g. Astro 4.0 Server Islands & SSG Docs\"\n              class=\"h-9 text-xs\"\n              required\n            />\n          </div>\n\n          <!-- Excerpt Description -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Excerpt & Notes</label>\n            <Input\n              v-model=\"formExcerpt\"\n              type=\"text\"\n              placeholder=\"Brief description or takeaway notes…\"\n              class=\"h-9 text-xs\"\n            />\n          </div>\n\n          <!-- Collection Selector -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Collection Folder</label>\n            <Select v-model=\"formCollection\">\n              <SelectTrigger class=\"h-9 text-xs\">\n                <SelectValue placeholder=\"Select collection\" />\n              </SelectTrigger>\n              <SelectContent>\n                <SelectItem value=\"design-systems\">Design Systems</SelectItem>\n                <SelectItem value=\"web-perf\">Web Performance</SelectItem>\n                <SelectItem value=\"typography\">Typography & Fonts</SelectItem>\n                <SelectItem value=\"ai-tools\">AI & LLM Tools</SelectItem>\n                <SelectItem value=\"security\">Security & Auth</SelectItem>\n              </SelectContent>\n            </Select>\n          </div>\n\n          <!-- Tags Input -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-xs font-medium\">Tags (comma separated)</label>\n            <Input v-model=\"formTags\" type=\"text\" placeholder=\"#astro, #vue, #web-perf\" class=\"h-9 text-xs\" />\n          </div>\n\n          <!-- Dialog Footer Buttons -->\n          <DialogFooter class=\"gap-2 pt-2 sm:gap-0\">\n            <DialogClose as-child>\n              <Button type=\"button\" variant=\"outline\" size=\"sm\" class=\"text-xs\"> Cancel </Button>\n            </DialogClose>\n            <Button type=\"submit\" size=\"sm\" class=\"text-xs font-medium\">\n              <Plus class=\"size-3.5\" />\n              Save Bookmark\n            </Button>\n          </DialogFooter>\n        </form>\n      </DialogContent>\n    </Dialog>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/BookmarkManagerGrid.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/dialog.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/select.json",
    "https://uipkge.dev/r/vue/separator.json"
  ],
  "description": "Raindrop style visual web bookmarks and engineering resources manager with collection folders, tag cloud filtering, real-time search, favorite toggles, copy link, and add bookmark modal.",
  "categories": [
    "productivity",
    "app",
    "dashboard",
    "collaboration"
  ]
}