{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "carousel",
  "title": "Carousel",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/carousel/Carousel.svelte",
      "content": "<!--\n  Carousel Component\n\n  A flexible carousel/slider for cycling through content, built on\n  `embla-carousel` (the same engine as the React twin's\n  `embla-carousel-react`). All parts are wired through Svelte context that\n  holds the shared `CarouselState`.\n\n  @example - Basic carousel with images\n  <Carousel>\n    <CarouselContent>\n      {#each images as img (img.id)}\n        <CarouselItem>\n          <img src={img.src} alt={img.alt} class=\"h-full w-full object-cover\" />\n        </CarouselItem>\n      {/each}\n    </CarouselContent>\n  </Carousel>\n\n  @example - Carousel with navigation controls\n  <Carousel opts={{ loop: true }}>\n    <CarouselContent>\n      {#each slides as slide (slide.id)}\n        <CarouselItem>\n          <SlideContent content={slide} />\n        </CarouselItem>\n      {/each}\n    </CarouselContent>\n    <CarouselFooter>\n      <CarouselPrevious />\n      <CarouselIndicators />\n      <CarouselNext />\n    </CarouselFooter>\n  </Carousel>\n\n  @example - Vertical carousel\n  <Carousel orientation=\"vertical\">\n    <CarouselContent>\n      {#each items as item (item.id)}\n        <CarouselItem>\n          {item}\n        </CarouselItem>\n      {/each}\n    </CarouselContent>\n  </Carousel>\n\n  @example - Controlled index + engine access\n  <Carousel bind:value={activeIndex} bind:api={embla}>\n    ...\n  </Carousel>\n-->\n<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n  import type { EmblaOptionsType, EmblaPluginType } from 'embla-carousel'\n  import type { CarouselApi } from './useCarousel.svelte'\n\n  export interface CarouselProps extends HTMLAttributes<HTMLDivElement> {\n    /** Embla options (`loop`, `align`, `slidesToScroll`, `dragFree`, …). React twin: `opts`. */\n    opts?: EmblaOptionsType\n    /** Embla plugins (e.g. `Autoplay()`). React twin: `plugins`. */\n    plugins?: EmblaPluginType[]\n    orientation?: 'horizontal' | 'vertical'\n    /** Shorthand for `opts.loop`. An explicit `opts.loop` wins. */\n    loop?: boolean\n    /** Controlled active slide index. Two-way bindable (`bind:value`). */\n    value?: number\n    /** The live embla engine. Two-way bindable (`bind:api`) — the Svelte equivalent of React's `setApi`. */\n    api?: CarouselApi | undefined\n    /** React-parity callback, fired when the engine initializes. Prefer `bind:api`. */\n    setApi?: (api: CarouselApi) => void\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { setContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { useCarousel, CAROUSEL_CONTEXT_KEY } from './useCarousel.svelte'\n  import { carouselVariants } from './carousel.variants'\n\n  let {\n    class: className,\n    opts,\n    plugins,\n    orientation = 'horizontal',\n    loop = false,\n    value = $bindable(0),\n    api = $bindable(undefined),\n    setApi,\n    children,\n    ref = $bindable(null),\n    onkeydowncapture,\n    ...restProps\n  }: CarouselProps = $props()\n\n  // Initial capture is intentional: opts/plugins/orientation/loop sync into\n  // the live engine via the $effect below (which re-inits embla on change).\n  // svelte-ignore state_referenced_locally\n  const carousel = useCarousel({ opts, plugins, orientation, loop })\n\n  // Provide to children\n  setContext(CAROUSEL_CONTEXT_KEY, carousel)\n\n  // Sync options into the shared state (re-inits the engine when attached).\n  $effect(() => {\n    carousel.orientation = orientation\n    carousel.syncOptions({ opts, plugins, loop })\n  })\n\n  // Publish the engine outward (bindable + React-style callback).\n  $effect(() => {\n    const engine = carousel.api\n    if (engine && api !== engine) {\n      api = engine\n      setApi?.(engine)\n    } else if (!engine && api !== undefined) {\n      api = undefined\n    }\n  })\n\n  // Controlled-index support: parent writes `value` → engine jumps (no\n  // animation, so mount/controlled updates never visibly sweep past slides)…\n  $effect(() => {\n    if (carousel.api && value !== carousel.selectedIndex) {\n      carousel.scrollTo(value, false)\n    }\n  })\n\n  // …and engine selection flows back to `value`.\n  $effect(() => {\n    const selected = carousel.selectedIndex\n    if (selected !== value) {\n      value = selected\n    }\n  })\n\n  // Arrow-key navigation — mirrors React's onKeyDownCapture.\n  type OnKeyDownCapture = NonNullable<HTMLAttributes<HTMLDivElement>['onkeydowncapture']>\n  const handleKeyDownCapture: OnKeyDownCapture = (e) => {\n    onkeydowncapture?.(e)\n    if (e.defaultPrevented) return\n    const isVertical = orientation === 'vertical'\n    if ((!isVertical && e.key === 'ArrowLeft') || (isVertical && e.key === 'ArrowUp')) {\n      e.preventDefault()\n      carousel.scrollPrev()\n    } else if ((!isVertical && e.key === 'ArrowRight') || (isVertical && e.key === 'ArrowDown')) {\n      e.preventDefault()\n      carousel.scrollNext()\n    }\n  }\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge\n  data-slot=\"carousel\"\n  data-orientation={orientation}\n  class={cn(carouselVariants({ orientation }), className)}\n  role=\"region\"\n  aria-roledescription=\"carousel\"\n  aria-label=\"Carousel\"\n  {...restProps}\n  onkeydowncapture={handleKeyDownCapture}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/Carousel.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselContent.svelte",
      "content": "<!--\n  CarouselContent\n\n  The embla viewport for carousel items. Mirrors the React twin's two-layer\n  structure: the outer div is the viewport the engine attaches to (fixed\n  scroll/snap classes), the inner div is the flex track that carries consumer\n  `class` / props / `ref`.\n\n  @example\n  <CarouselContent>\n    {#each slides as slide (slide.id)}\n      <CarouselItem>\n        <img src={slide.src} alt={slide.alt} />\n      </CarouselItem>\n    {/each}\n  </CarouselContent>\n-->\n<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CarouselContentProps extends HTMLAttributes<HTMLDivElement> {\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { CAROUSEL_CONTEXT_KEY, type CarouselState } from './useCarousel.svelte'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: CarouselContentProps = $props()\n\n  const carousel = getContext<CarouselState | undefined>(CAROUSEL_CONTEXT_KEY)\n\n  let viewport: HTMLDivElement | null = $state(null)\n\n  $effect(() => {\n    if (viewport && carousel) {\n      carousel.attach(viewport)\n      return () => carousel.detach()\n    }\n  })\n\n  const isHorizontal = $derived(carousel?.orientation !== 'vertical')\n\n  // Class strings are 1:1 with the React twin's inlined snap classes.\n  const scrollClass = $derived(\n    isHorizontal\n      ? 'flex snap-x snap-mandatory overflow-x-auto scroll-smooth'\n      : 'flex snap-y snap-mandatory flex-col overflow-y-auto',\n  )\n</script>\n\n<!-- svelte-ignore a11y_role_supports_aria_props: Vue twin carries the same role + aria-orientation pair -->\n<div\n  bind:this={viewport}\n  data-uipkge\n  data-slot=\"carousel-content\"\n  class={cn(\n    scrollClass,\n    'relative h-full w-full',\n    '[-ms-overflow-style:none] [scrollbar-width:none]',\n    '[&::-webkit-scrollbar]:hidden',\n  )}\n  aria-orientation={carousel?.orientation}\n  role=\"group\"\n>\n  <div\n    bind:this={ref}\n    class={cn(\n      'flex',\n      // w-full clamps the track to the viewport width. Without it the track's\n      // min-width:auto sizes to the slides' intrinsic content and overflows the\n      // viewport, so basis-full slides resolve against the wider track and\n      // render too big. Keeps the two frameworks 1:1.\n      isHorizontal ? 'w-full' : 'flex-col',\n      className,\n    )}\n    {...restProps}\n  >\n    {@render children?.()}\n  </div>\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselContent.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselFooter.svelte",
      "content": "<!--\n  CarouselFooter\n\n  Wrapper for carousel footer content (pagination, indicators).\n\n  @example\n  <CarouselFooter>\n    <CarouselIndicators />\n  </CarouselFooter>\n-->\n<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CarouselFooterProps extends HTMLAttributes<HTMLDivElement> {\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: CarouselFooterProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge\n  data-slot=\"carousel-footer\"\n  class={cn('flex items-center justify-between px-1 pt-2', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselFooter.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselHeader.svelte",
      "content": "<!--\n  CarouselHeader\n\n  Wrapper for carousel header content (title, controls).\n\n  @example\n  <CarouselHeader>\n    <h3>Featured Products</h3>\n  </CarouselHeader>\n-->\n<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CarouselHeaderProps extends HTMLAttributes<HTMLDivElement> {\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { cn } from '$lib/utils'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: CarouselHeaderProps = $props()\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge\n  data-slot=\"carousel-header\"\n  class={cn('flex items-center justify-between px-1 pb-2', className)}\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselHeader.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselIndicators.svelte",
      "content": "<!--\n  CarouselIndicators\n\n  Pagination dots showing current slide position.\n  Clicking a dot navigates to that slide.\n\n  @example\n  <CarouselIndicators />\n-->\n<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CarouselIndicatorsProps extends HTMLAttributes<HTMLDivElement> {\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { CAROUSEL_CONTEXT_KEY, type CarouselState } from './useCarousel.svelte'\n\n  let { class: className, ref = $bindable(null), ...restProps }: CarouselIndicatorsProps = $props()\n\n  const carousel = getContext<CarouselState | undefined>(CAROUSEL_CONTEXT_KEY)\n</script>\n\n{#if carousel?.api}\n  <div\n    bind:this={ref}\n    data-uipkge\n    data-slot=\"carousel-indicators\"\n    class={cn('flex items-center justify-center gap-1.5 py-2', className)}\n    role=\"tablist\"\n    aria-label=\"Carousel navigation\"\n    {...restProps}\n  >\n    {#each carousel.scrollSnaps as _, index (index)}\n      <button\n        type=\"button\"\n        role=\"tab\"\n        aria-label={`Go to slide ${index + 1}`}\n        aria-selected={index === carousel.selectedIndex}\n        class={cn(\n          'h-2 w-2 rounded-full transition-colors duration-200',\n          index === carousel.selectedIndex ? 'bg-primary w-6' : 'bg-muted-foreground/30 hover:bg-muted-foreground/50',\n        )}\n        onclick={() => carousel.scrollTo(index, true)}\n      ></button>\n    {/each}\n  </div>\n{/if}\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselIndicators.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselItem.svelte",
      "content": "<!--\n  CarouselItem\n\n  Individual slide within the carousel.\n  Each item snaps into view when scrolled.\n\n  @example\n  <CarouselItem>\n    <img src=\"/img1.jpg\" alt=\"Slide 1\" />\n  </CarouselItem>\n-->\n<script lang=\"ts\" module>\n  import type { HTMLAttributes } from 'svelte/elements'\n\n  export interface CarouselItemProps extends HTMLAttributes<HTMLDivElement> {\n    ref?: HTMLDivElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { cn } from '$lib/utils'\n  import { carouselItemVariants } from './carousel.variants'\n  import { CAROUSEL_CONTEXT_KEY, type CarouselState } from './useCarousel.svelte'\n\n  let { class: className, children, ref = $bindable(null), ...restProps }: CarouselItemProps = $props()\n\n  const carousel = getContext<CarouselState | undefined>(CAROUSEL_CONTEXT_KEY)\n\n  const itemClass = $derived(carouselItemVariants({ orientation: carousel?.orientation ?? 'horizontal' }))\n</script>\n\n<div\n  bind:this={ref}\n  data-uipkge\n  data-slot=\"carousel-item\"\n  class={cn(itemClass, 'snap-start', 'relative', className)}\n  role=\"group\"\n  aria-roledescription=\"slide\"\n  {...restProps}\n>\n  {@render children?.()}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselItem.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselNext.svelte",
      "content": "<!--\n  CarouselNext\n\n  Navigation button to go to the next slide.\n\n  @example\n  <CarouselNext label=\"Next slide\" />\n-->\n<script lang=\"ts\" module>\n  import type { HTMLButtonAttributes } from 'svelte/elements'\n\n  export interface CarouselNextProps extends HTMLButtonAttributes {\n    label?: string\n    ref?: HTMLButtonElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { ChevronRight } from '@lucide/svelte'\n  import { cn } from '$lib/utils'\n  import { Button } from '$lib/components/ui/button'\n  import { CAROUSEL_CONTEXT_KEY, type CarouselState } from './useCarousel.svelte'\n\n  let {\n    class: className,\n    label = 'Next slide',\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: CarouselNextProps = $props()\n\n  const carousel = getContext<CarouselState | undefined>(CAROUSEL_CONTEXT_KEY)\n\n  const isHorizontal = $derived(carousel?.orientation !== 'vertical')\n</script>\n\n<Button\n  type=\"button\"\n  variant=\"outline\"\n  size=\"icon\"\n  bind:ref\n  class={cn(\n    'absolute z-10 size-8 shrink-0 rounded-full',\n    'bg-background/80 border shadow-md backdrop-blur-sm',\n    'hover:bg-accent hover:text-accent-foreground',\n    'disabled:pointer-events-none disabled:opacity-50',\n    isHorizontal ? 'top-1/2 -right-3 -translate-y-1/2' : '-bottom-3 left-1/2 -translate-x-1/2 rotate-90',\n    className,\n  )}\n  disabled={!carousel?.canScrollNext}\n  aria-label={label}\n  onclick={() => carousel?.scrollNext()}\n  {...restProps}\n>\n  {#if children}\n    {@render children()}\n  {:else}\n    <ChevronRight class=\"size-4\" aria-hidden=\"true\" />\n  {/if}\n</Button>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselNext.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/CarouselPrevious.svelte",
      "content": "<!--\n  CarouselPrevious\n\n  Navigation button to go to the previous slide.\n\n  @example\n  <CarouselPrevious label=\"Previous slide\" />\n-->\n<script lang=\"ts\" module>\n  import type { HTMLButtonAttributes } from 'svelte/elements'\n\n  export interface CarouselPreviousProps extends HTMLButtonAttributes {\n    label?: string\n    ref?: HTMLButtonElement | null\n  }\n</script>\n\n<script lang=\"ts\">\n  import { getContext } from 'svelte'\n  import { ChevronLeft } from '@lucide/svelte'\n  import { cn } from '$lib/utils'\n  import { Button } from '$lib/components/ui/button'\n  import { CAROUSEL_CONTEXT_KEY, type CarouselState } from './useCarousel.svelte'\n\n  let {\n    class: className,\n    label = 'Previous slide',\n    children,\n    ref = $bindable(null),\n    ...restProps\n  }: CarouselPreviousProps = $props()\n\n  const carousel = getContext<CarouselState | undefined>(CAROUSEL_CONTEXT_KEY)\n\n  const isHorizontal = $derived(carousel?.orientation !== 'vertical')\n</script>\n\n<Button\n  type=\"button\"\n  variant=\"outline\"\n  size=\"icon\"\n  bind:ref\n  class={cn(\n    'absolute z-10 size-8 shrink-0 rounded-full',\n    'bg-background/80 border shadow-md backdrop-blur-sm',\n    'hover:bg-accent hover:text-accent-foreground',\n    'disabled:pointer-events-none disabled:opacity-50',\n    isHorizontal ? 'top-1/2 -left-3 -translate-y-1/2' : '-top-3 left-1/2 -translate-x-1/2 rotate-90',\n    className,\n  )}\n  disabled={!carousel?.canScrollPrev}\n  aria-label={label}\n  onclick={() => carousel?.scrollPrev()}\n  {...restProps}\n>\n  {#if children}\n    {@render children()}\n  {:else}\n    <ChevronLeft class=\"size-4\" aria-hidden=\"true\" />\n  {/if}\n</Button>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/CarouselPrevious.svelte"
    },
    {
      "path": "packages/registry-svelte/components/carousel/carousel.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts` or inline in the component) so `Carousel.svelte` /\n * `CarouselItem.svelte` can import them without creating a circular\n * dependency back through the index.\n *\n * The class strings are 1:1 with the React twin's inlined orientation\n * classes (`relative overflow-hidden` + `w-full` / `h-full flex-col` on the\n * root; `flex shrink-0 grow-0 basis-full flex-col` + `w-full` / `h-full` on\n * the item). The `snap-*` utilities stay inlined in `CarouselContent.svelte`\n * / `CarouselItem.svelte`, exactly where React inlines them.\n */\nexport const carouselVariants = cva('relative overflow-hidden', {\n  variants: {\n    orientation: {\n      horizontal: 'w-full',\n      vertical: 'h-full flex-col',\n    },\n  },\n  defaultVariants: {\n    orientation: 'horizontal',\n  },\n})\n\nexport const carouselItemVariants = cva('flex shrink-0 grow-0 basis-full flex-col', {\n  variants: {\n    orientation: {\n      horizontal: 'w-full',\n      vertical: 'h-full',\n    },\n  },\n  defaultVariants: {\n    orientation: 'horizontal',\n  },\n})\n\nexport type CarouselVariants = VariantProps<typeof carouselVariants>\nexport type CarouselItemVariants = VariantProps<typeof carouselItemVariants>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/carousel.variants.ts"
    },
    {
      "path": "packages/registry-svelte/components/carousel/useCarousel.svelte.ts",
      "content": "/**\n * useCarousel\n *\n * Embla-backed carousel state for Svelte 5 runes. Mirrors the React twin\n * (`embla-carousel-react` + `useCarousel` context) 1:1: `Carousel.svelte`\n * owns the options and shares the returned state with its parts via Svelte\n * context; `CarouselContent` attaches the embla viewport, which creates the\n * engine.\n *\n * Why the framework-agnostic `embla-carousel` core instead of the\n * `embla-carousel-svelte` action wrapper: the wrapper's\n * `use:emblaCarouselSvelte` + `on:emblainit` custom-event pattern is Svelte-4\n * era (`on:` is deprecated in runes mode) and splits awkwardly across the\n * Carousel (owns opts/plugins) / CarouselContent (owns the viewport node)\n * boundary. The core is the same engine `embla-carousel-react` wraps, so\n * behavior (`loop`, `align`, `slidesToScroll`, `dragFree`, plugins such as\n * Autoplay) is identical across frameworks.\n *\n * Must be called during component initialization (like the Vue composable):\n * it registers an `onDestroy` cleanup.\n */\nimport EmblaCarousel, { type EmblaCarouselType, type EmblaOptionsType, type EmblaPluginType } from 'embla-carousel'\nimport { getContext, onDestroy } from 'svelte'\n\n/** The live embla engine. React twin: `CarouselApi` in `carousel.tsx`. */\nexport type CarouselApi = EmblaCarouselType\n\nexport interface CarouselOptions {\n  /** Embla options (`loop`, `align`, `slidesToScroll`, `dragFree`, …). React twin: `CarouselProps['opts']`. */\n  opts?: EmblaOptionsType\n  /** Embla plugins (e.g. `Autoplay()`). React twin: `CarouselProps['plugins']`. */\n  plugins?: EmblaPluginType[]\n  orientation?: 'horizontal' | 'vertical'\n  /** Shorthand for `opts.loop`. An explicit `opts.loop` wins. Kept from the scroll-snap era. */\n  loop?: boolean\n}\n\nexport const CAROUSEL_CONTEXT_KEY = 'carousel'\n\nexport class CarouselState {\n  /** The live embla engine once `CarouselContent` has attached, else `undefined`. `$state.raw` — never proxy an external class instance. */\n  api = $state.raw<EmblaCarouselType | undefined>(undefined)\n  canScrollPrev = $state(false)\n  canScrollNext = $state(false)\n  selectedIndex = $state(0)\n  scrollSnaps = $state<number[]>([])\n  orientation = $state<'horizontal' | 'vertical'>('horizontal')\n\n  #viewport: HTMLElement | null = null\n  #opts: EmblaOptionsType = {}\n  #plugins: EmblaPluginType[] | undefined = undefined\n  #onSelect = () => this.#syncFromApi()\n\n  constructor(options: CarouselOptions = {}) {\n    this.orientation = options.orientation ?? 'horizontal'\n    this.#storeOptions(options)\n  }\n\n  /** Back-compat alias for the scroll-snap era's `activeIndex`. Prefer `selectedIndex`. */\n  get activeIndex(): number {\n    return this.selectedIndex\n  }\n\n  /** Back-compat alias for the scroll-snap era's `loop`. */\n  get loop(): boolean {\n    return !!this.#opts.loop\n  }\n\n  set loop(value: boolean) {\n    this.syncOptions({ loop: value })\n  }\n\n  /** Back-compat manual scroll-sync method from scroll-snap era. */\n  updateScrollState(): void {\n    this.#syncFromApi()\n  }\n\n  /** The embla viewport node. React twin: `carouselRef`. */\n  get viewport(): HTMLElement | null {\n    return this.#viewport\n  }\n\n  get isHorizontal(): boolean {\n    return this.orientation === 'horizontal'\n  }\n\n  #storeOptions(options: Pick<CarouselOptions, 'opts' | 'plugins' | 'loop'>): void {\n    const nextOpts = options.opts ?? {}\n    this.#opts =\n      options.loop !== undefined && nextOpts.loop === undefined ? { ...nextOpts, loop: options.loop } : nextOpts\n    this.#plugins = options.plugins\n  }\n\n  #mergedOpts(): EmblaOptionsType {\n    // Orientation always wins over `opts.axis` — matches React (`{ ...opts, axis }`).\n    return { ...this.#opts, axis: this.orientation === 'horizontal' ? 'x' : 'y' }\n  }\n\n  #syncFromApi(): void {\n    const api = this.api\n    if (!api) return\n    // One handler covers both React effects (`onSelect` in Carousel + `onUpdate` in Indicators).\n    this.canScrollPrev = api.canScrollPrev()\n    this.canScrollNext = api.canScrollNext()\n    this.scrollSnaps = api.scrollSnapList()\n    this.selectedIndex = api.selectedScrollSnap()\n  }\n\n  /** Create the engine on `viewport`. Called by `CarouselContent` once its scrollable root mounts. */\n  attach(viewport: HTMLElement): void {\n    if (this.#viewport === viewport && this.api) return\n    this.detach()\n    this.#viewport = viewport\n    const api = EmblaCarousel(viewport, this.#mergedOpts(), this.#plugins)\n    this.api = api\n    this.#syncFromApi()\n    api.on('select', this.#onSelect)\n    api.on('reInit', this.#onSelect)\n  }\n\n  detach(): void {\n    if (this.api) {\n      this.api.off('select', this.#onSelect)\n      this.api.off('reInit', this.#onSelect)\n      this.api.destroy()\n      this.api = undefined\n    }\n    this.#viewport = null\n  }\n\n  /** Push new opts/plugins into the live engine (re-inits embla). Called by `Carousel` when props change. */\n  syncOptions(options: Pick<CarouselOptions, 'opts' | 'plugins' | 'loop'>): void {\n    this.#storeOptions(options)\n    this.api?.reInit(this.#mergedOpts(), this.#plugins)\n    this.#syncFromApi()\n  }\n\n  scrollPrev(): void {\n    this.api?.scrollPrev()\n  }\n\n  scrollNext(): void {\n    this.api?.scrollNext()\n  }\n\n  scrollTo(index: number, smooth = true): void {\n    // Embla's 2nd arg is `jump` (instant) — the inverse of `smooth`.\n    this.api?.scrollTo(index, !smooth)\n  }\n\n  /**\n   * @deprecated Use `scrollPrev` (React parity). Kept for scroll-snap-era\n   * consumers — the `smooth` arg is accepted for signature compat and ignored\n   * (embla always animates prev/next).\n   */\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  scrollToPrev(smooth = true): void {\n    this.scrollPrev()\n  }\n\n  /**\n   * @deprecated Use `scrollNext` (React parity). Kept for scroll-snap-era\n   * consumers — the `smooth` arg is accepted for signature compat and ignored\n   * (embla always animates prev/next).\n   */\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  scrollToNext(smooth = true): void {\n    this.scrollNext()\n  }\n}\n\nexport function useCarousel(options: CarouselOptions = {}): CarouselState {\n  const state = new CarouselState(options)\n\n  onDestroy(() => {\n    state.detach()\n  })\n\n  return state\n}\n\n/** Read the carousel state from any sub-part. Throws outside `<Carousel />` — mirrors React's `useCarousel`. */\nexport function getCarouselContext(): CarouselState {\n  const state = getContext<CarouselState | undefined>(CAROUSEL_CONTEXT_KEY)\n  if (!state) {\n    throw new Error('useCarousel must be used within a <Carousel />')\n  }\n  return state\n}\n\n/** @deprecated Use `CarouselState`. Renamed to free `CarouselApi` for the live Embla engine (React parity). */\nexport const CarouselApiClass = CarouselState\nexport type CarouselApiState = CarouselState\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/useCarousel.svelte.ts"
    },
    {
      "path": "packages/registry-svelte/components/carousel/index.ts",
      "content": "export { default as Carousel, type CarouselProps } from './Carousel.svelte'\nexport { default as CarouselContent, type CarouselContentProps } from './CarouselContent.svelte'\nexport { default as CarouselItem, type CarouselItemProps } from './CarouselItem.svelte'\nexport { default as CarouselPrevious, type CarouselPreviousProps } from './CarouselPrevious.svelte'\nexport { default as CarouselNext, type CarouselNextProps } from './CarouselNext.svelte'\nexport { default as CarouselIndicators, type CarouselIndicatorsProps } from './CarouselIndicators.svelte'\nexport { default as CarouselHeader, type CarouselHeaderProps } from './CarouselHeader.svelte'\nexport { default as CarouselFooter, type CarouselFooterProps } from './CarouselFooter.svelte'\n\n// Re-export variant API from the sibling file (kept separate to avoid the\n// Carousel.svelte <-> index.ts circular import that broke dev SSR in Vue).\nexport {\n  carouselVariants,\n  carouselItemVariants,\n  type CarouselVariants,\n  type CarouselItemVariants,\n} from './carousel.variants'\n\nexport {\n  useCarousel,\n  getCarouselContext,\n  CarouselState,\n  CAROUSEL_CONTEXT_KEY,\n  type CarouselApi,\n  type CarouselOptions,\n} from './useCarousel.svelte'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/carousel/index.ts"
    }
  ],
  "dependencies": [
    "@lucide/svelte",
    "class-variance-authority",
    "embla-carousel"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/svelte/button.json"
  ],
  "description": "Horizontal or vertical scroller with previous/next controls. Built on embla-carousel (the same engine as the React twin) and wired through Svelte context. Drop in images, cards, or any custom slide content.",
  "categories": [
    "data-display"
  ]
}