{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "announcement-banner",
  "title": "Announcement Banner",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/announcement-banner/AnnouncementBanner.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { ArrowRight, ChevronLeft, ChevronRight, Megaphone, Terminal, X } from 'lucide-vue-next'\nimport { Badge } from '@/components/ui/badge'\nimport { cn } from '@/lib/utils'\n\ninterface AnnouncementItem {\n  id: string\n  category: 'release' | 'security' | 'maintenance' | 'feature'\n  badgeText: string\n  title: string\n  actionLabel: string\n  actionUrl?: string\n  cliSnippet?: string\n}\n\ninterface Props {\n  variant?: 'floating-pill' | 'top-bar' | 'interactive-ticker'\n  autoplay?: boolean\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  variant: 'interactive-ticker',\n  autoplay: true,\n})\n\nconst announcements: AnnouncementItem[] = [\n  {\n    id: 'ann-1',\n    category: 'release',\n    badgeText: 'v2.4.0 Release',\n    title: 'Tailwind CSS v4 OKLCH tokens & 281+ production workbenches are live.',\n    actionLabel: 'Read Changelog',\n    actionUrl: 'https://uipkge.dev/changelog',\n    cliSnippet: 'npx shadcn-vue@latest add https://uipkge.dev/r/vue/init.json',\n  },\n  {\n    id: 'ann-2',\n    category: 'feature',\n    badgeText: 'Dual-Framework',\n    title: '100% Vue 3.5 & React 19 component parity achieved with zero runtime CSS.',\n    actionLabel: 'Explore Parity Matrix',\n    actionUrl: 'https://uipkge.dev/components',\n    cliSnippet: 'npx shadcn@latest add https://uipkge.dev/r/react/init.json',\n  },\n  {\n    id: 'ann-3',\n    category: 'security',\n    badgeText: 'Security Notice',\n    title: 'Upstream Reka UI & Radix dependency security patches verified and updated.',\n    actionLabel: 'Security Advisory',\n    actionUrl: 'https://uipkge.dev/security',\n  },\n]\n\nconst currentIndex = ref(0)\nconst dismissed = ref(false)\nconst isPaused = ref(false)\nconst copiedCli = ref(false)\n\nlet timer: ReturnType<typeof setInterval> | undefined\n\nfunction startAutoplay() {\n  if (!props.autoplay) return\n  stopAutoplay()\n  timer = setInterval(() => {\n    if (!isPaused.value) {\n      nextAnnouncement()\n    }\n  }, 6000)\n}\n\nfunction stopAutoplay() {\n  if (timer) clearInterval(timer)\n}\n\nonMounted(() => {\n  startAutoplay()\n})\n\nonUnmounted(() => {\n  stopAutoplay()\n})\n\nfunction nextAnnouncement() {\n  currentIndex.value = (currentIndex.value + 1) % announcements.length\n}\n\nfunction prevAnnouncement() {\n  currentIndex.value = (currentIndex.value - 1 + announcements.length) % announcements.length\n}\n\nfunction copyCliSnippet(snippet?: string) {\n  if (!snippet) return\n  navigator.clipboard.writeText(snippet)\n  copiedCli.value = true\n  setTimeout(() => (copiedCli.value = false), 2000)\n}\n\nconst current = computed(() => announcements[currentIndex.value])\n</script>\n\n<template>\n  <div\n    v-if=\"!dismissed\"\n    data-slot=\"announcement-banner\"\n    :data-variant=\"props.variant\"\n    role=\"region\"\n    aria-label=\"Announcement\"\n    :class=\"cn('relative z-50 transition-colors duration-200', props.class)\"\n    @mouseenter=\"isPaused = true\"\n    @mouseleave=\"isPaused = false\"\n  >\n    <!-- 1. Interactive Ticker Mode (Default) -->\n    <div\n      v-if=\"props.variant === 'interactive-ticker'\"\n      class=\"bg-muted/70 border-border text-foreground flex items-center justify-between gap-3 overflow-hidden border-b px-4 py-2 text-xs shadow-xs backdrop-blur-md\"\n    >\n      <div class=\"mx-auto flex w-full max-w-6xl items-center justify-between gap-4\">\n        <!-- Left: Status Beacon & Item Indicator -->\n        <div class=\"flex shrink-0 items-center gap-3\">\n          <div class=\"text-muted-foreground flex items-center gap-1.5 font-mono text-xs\">\n            <span class=\"bg-primary size-2 animate-pulse rounded-full\" />\n            <span class=\"text-foreground font-semibold\">Live Dispatch</span>\n          </div>\n\n          <Badge\n            variant=\"secondary\"\n            class=\"px-2 py-0.5 font-mono text-xs\"\n            :class=\"[\n              current.category === 'release' && 'bg-primary/10 text-primary border-primary/20',\n              current.category === 'security' && 'border-warning/20 bg-warning/10 text-warning',\n              current.category === 'feature' && 'border-success/20 bg-success/10 text-success',\n            ]\"\n          >\n            {{ current.badgeText }}\n          </Badge>\n        </div>\n\n        <!-- Center: Dynamic Headline & Action Link -->\n        <div class=\"flex flex-1 items-center justify-center gap-2 truncate\">\n          <p class=\"text-foreground truncate font-medium\">\n            {{ current.title }}\n          </p>\n          <a\n            v-if=\"current.actionUrl\"\n            :href=\"current.actionUrl\"\n            class=\"text-primary hidden shrink-0 items-center gap-1 font-semibold hover:underline sm:inline-flex\"\n          >\n            {{ current.actionLabel }} <ArrowRight class=\"size-3\" />\n          </a>\n        </div>\n\n        <!-- Right: Ticker Navigation & CLI / Dismiss -->\n        <div class=\"flex shrink-0 items-center gap-2\">\n          <button\n            v-if=\"current.cliSnippet\"\n            type=\"button\"\n            class=\"bg-background border-border text-muted-foreground hover:text-foreground hidden items-center gap-1.5 rounded border px-2 py-0.5 font-mono text-xs transition-colors md:inline-flex\"\n            @click=\"copyCliSnippet(current.cliSnippet)\"\n          >\n            <Terminal class=\"text-primary size-3\" />\n            <span>{{ copiedCli ? 'Copied CLI!' : 'Copy CLI' }}</span>\n          </button>\n\n          <div class=\"border-border bg-background flex items-center overflow-hidden rounded-md border\">\n            <button\n              type=\"button\"\n              class=\"hover:bg-muted text-muted-foreground hover:text-foreground p-1 transition-colors\"\n              aria-label=\"Previous announcement\"\n              @click=\"prevAnnouncement\"\n            >\n              <ChevronLeft class=\"size-3.5\" />\n            </button>\n            <span class=\"text-muted-foreground px-1.5 font-mono text-xs\">\n              {{ currentIndex + 1 }}/{{ announcements.length }}\n            </span>\n            <button\n              type=\"button\"\n              class=\"hover:bg-muted text-muted-foreground hover:text-foreground p-1 transition-colors\"\n              aria-label=\"Next announcement\"\n              @click=\"nextAnnouncement\"\n            >\n              <ChevronRight class=\"size-3.5\" />\n            </button>\n          </div>\n\n          <button\n            type=\"button\"\n            aria-label=\"Dismiss banner\"\n            class=\"text-muted-foreground hover:text-foreground hover:bg-muted rounded-md p-1 transition-colors\"\n            @click=\"dismissed = true\"\n          >\n            <X class=\"size-3.5\" />\n          </button>\n        </div>\n      </div>\n    </div>\n\n    <!-- 2. Floating Pill Mode -->\n    <div v-else-if=\"props.variant === 'floating-pill'\" class=\"mx-auto w-fit max-w-2xl px-4 py-2\">\n      <div\n        class=\"bg-card/90 border-border text-foreground hover:border-primary/40 flex items-center gap-3 rounded-full border py-1.5 pr-2 pl-3 text-xs shadow-md backdrop-blur-md transition-colors\"\n      >\n        <Badge variant=\"secondary\" class=\"bg-primary/10 text-primary border-primary/20 px-2 py-0.5 font-mono text-xs\">\n          {{ current.badgeText }}\n        </Badge>\n        <p class=\"text-foreground max-w-sm truncate font-medium\">\n          {{ current.title }}\n        </p>\n        <a\n          v-if=\"current.actionUrl\"\n          :href=\"current.actionUrl\"\n          class=\"text-primary inline-flex shrink-0 items-center gap-1 font-semibold hover:underline\"\n        >\n          {{ current.actionLabel }} <ArrowRight class=\"size-3\" />\n        </a>\n        <button\n          type=\"button\"\n          aria-label=\"Dismiss announcement\"\n          class=\"text-muted-foreground hover:text-foreground hover:bg-muted rounded-full p-1 transition-colors\"\n          @click=\"dismissed = true\"\n        >\n          <X class=\"size-3.5\" />\n        </button>\n      </div>\n    </div>\n\n    <!-- 3. Top Solid Bar Mode -->\n    <div\n      v-else\n      class=\"bg-primary text-primary-foreground flex items-center justify-between gap-3 px-4 py-2 text-xs shadow-xs\"\n    >\n      <div class=\"mx-auto flex w-full max-w-6xl items-center justify-between gap-4\">\n        <div class=\"flex items-center gap-2 truncate\">\n          <Megaphone class=\"size-4 shrink-0\" />\n          <span class=\"font-mono font-bold\">[{{ current.badgeText }}]</span>\n          <span class=\"truncate\">{{ current.title }}</span>\n        </div>\n        <div class=\"flex shrink-0 items-center gap-3\">\n          <a\n            v-if=\"current.actionUrl\"\n            :href=\"current.actionUrl\"\n            class=\"inline-flex items-center gap-1 font-semibold underline-offset-4 hover:underline\"\n          >\n            {{ current.actionLabel }} <ArrowRight class=\"size-3\" />\n          </a>\n          <button\n            type=\"button\"\n            aria-label=\"Dismiss banner\"\n            class=\"text-primary-foreground/80 hover:text-primary-foreground hover:bg-primary-foreground/15 rounded p-1 transition-colors\"\n            @click=\"dismissed = true\"\n          >\n            <X class=\"size-3.5\" />\n          </button>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/AnnouncementBanner.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json"
  ],
  "description": "Interactive release, security, and maintenance broadcast center with rotating live ticker, CLI copy button, and floating pill / top-bar modes.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "announcement"
}