{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "timeline",
  "title": "Timeline",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/timeline/timeline.component.ts",
      "content": "import {\n  Component,\n  DestroyRef,\n  ElementRef,\n  Input,\n  ViewEncapsulation,\n  booleanAttribute,\n  computed,\n  inject,\n  signal,\n  ChangeDetectionStrategy,\n} from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { timelineMediaVariants, type TimelineMediaVariant, type TimelineMediaVariantsProps } from './timeline.variants'\n\n// Copied verbatim from the React timeline (injected there as a global <style> by TimelineItem).\n// Unscoped (ViewEncapsulation.None) so it reaches the markers; Angular adds it once.\nconst TIMELINE_MOTION_STYLES = `\n@keyframes timeline-item-enter {\n  from { opacity: 0; transform: translateY(6px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n@keyframes timeline-current-pulse {\n  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklab, var(--primary) 45%, transparent); }\n  50% { box-shadow: 0 0 0 6px color-mix(in oklab, var(--primary) 0%, transparent); }\n}\n[data-slot='timeline-item'].timeline-item-enter {\n  animation: timeline-item-enter 320ms cubic-bezier(0.22, 1, 0.36, 1) both;\n  animation-delay: var(--timeline-stagger, 0ms);\n}\n[data-slot='timeline-item'].timeline-item-current [data-slot='timeline-media-marker'],\n[data-slot='timeline-item'].timeline-item-current [data-slot='timeline-separator-marker'] {\n  animation: timeline-current-pulse 1.8s ease-in-out infinite;\n}\n@media (prefers-reduced-motion: reduce) {\n  [data-slot='timeline-item'].timeline-item-enter,\n  [data-slot='timeline-item'].timeline-item-current [data-slot='timeline-media-marker'],\n  [data-slot='timeline-item'].timeline-item-current [data-slot='timeline-separator-marker'] {\n    animation: none !important;\n  }\n}\n`\n\nexport type TimelineDirection = 'vertical' | 'horizontal'\nexport type TimelineAlign = 'start' | 'center'\nexport type TimelineSide = 'left' | 'right' | 'top' | 'bottom'\nexport type TimelineStatus = 'default' | 'current' | 'success' | 'warning' | 'error' | 'info' | 'muted'\nexport type TimelineDensity = 'compact' | 'default' | 'comfortable'\nexport type TimelineMediaStatus = NonNullable<TimelineMediaVariantsProps['status']>\nexport type TimelineLineStyle = 'solid' | 'dashed' | 'dotted'\nexport type TimelineTitleAs = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div'\n\n/** React `TimelineItemRenderProps`, read from `#item=\"uiTimelineItem\"` in Angular. */\nexport interface TimelineItemRenderProps {\n  index: number\n  isLast: boolean\n  side: TimelineSide\n  status: TimelineStatus\n}\n\n/**\n * Angular port of UIPKGE Timeline (React `Timeline`). The root provides direction / align /\n * side / density to its items, which register themselves (in DOM order) so each item knows\n * its index and whether it is the last one: that drives the alternating sides of\n * `align=\"center\"`, the row spacing (none after the last row) and the connector lines (none\n * below the last marker). Class strings, data attributes and motion CSS match React.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline, [ui-timeline]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline\"',\n    '[attr.data-direction]': 'direction',\n    '[attr.data-align]': 'align',\n    // `align` is consumed like the React prop: a leftover HTML align=\"center\" would center the text.\n    '[attr.align]': 'null',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineComponent {\n  @Input() direction: TimelineDirection = 'vertical'\n  @Input() align: TimelineAlign = 'start'\n  @Input() side?: TimelineSide\n  @Input() density: TimelineDensity = 'default'\n  @Input('class') className?: string\n\n  /** Registered items in DOM order (signal: registration happens outside template events). */\n  readonly items = signal<UiTimelineItemComponent[]>([])\n  private sortQueued = false\n\n  /** Explicit side wins, else top for horizontal / left for vertical. */\n  get resolvedSide(): TimelineSide {\n    return this.side ?? (this.direction === 'horizontal' ? 'top' : 'left')\n  }\n\n  register(item: UiTimelineItemComponent): void {\n    this.items.update((prev) => (prev.includes(item) ? prev : [...prev, item]))\n    this.queueSort()\n  }\n\n  unregister(item: UiTimelineItemComponent): void {\n    this.items.update((prev) => prev.filter((i) => i !== item))\n  }\n\n  indexOf(item: UiTimelineItemComponent): number {\n    return this.items().indexOf(item)\n  }\n\n  /** Items register on creation, before projection places them; re-sort once they are in the DOM. */\n  private queueSort(): void {\n    if (this.sortQueued) return\n    this.sortQueued = true\n    queueMicrotask(() => {\n      this.sortQueued = false\n      const prev = this.items()\n      const sorted = [...prev].sort((a, b) =>\n        a.el.compareDocumentPosition(b.el) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1,\n      )\n      if (sorted.some((item, i) => item !== prev[i])) this.items.set(sorted)\n    })\n  }\n\n  get hostClass(): string {\n    return cn('relative', this.direction === 'vertical' ? 'flex flex-col' : 'flex flex-row', this.className)\n  }\n}\n\nconst VERTICAL_SPACING: Record<TimelineDensity, string> = {\n  compact: '[&>[data-slot=timeline-content]]:pb-2',\n  default: '[&>[data-slot=timeline-content]]:pb-6',\n  comfortable: '[&>[data-slot=timeline-content]]:pb-10',\n}\n\nconst HORIZONTAL_SPACING: Record<TimelineDensity, string> = {\n  compact: '[&>[data-slot=timeline-content]]:pr-3',\n  default: '[&>[data-slot=timeline-content]]:pr-6',\n  comfortable: '[&>[data-slot=timeline-content]]:pr-10',\n}\n\n/**\n * React `TimelineItem`. Reads index / last / side from the root; `status` tints its media\n * marker and `current` pulses it. React passes render props to a function child; in Angular\n * read them from the exported instance: `<div ui-timeline-item #item=\"uiTimelineItem\">\n * {{ item.index() }} {{ item.isLast() }}</div>`.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-item, [ui-timeline-item]',\n  exportAs: 'uiTimelineItem',\n  standalone: true,\n  encapsulation: ViewEncapsulation.None,\n  styles: [TIMELINE_MOTION_STYLES],\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-item\"',\n    '[attr.data-side]': 'effectiveSide',\n    '[attr.data-status]': 'status',\n    '[attr.data-last]': 'isLast() || null',\n    '[style.--timeline-stagger]': 'stagger',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineItemComponent {\n  readonly timeline = inject(UiTimelineComponent, { optional: true })\n  readonly el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement\n\n  @Input() side?: TimelineSide\n  @Input() status: TimelineStatus = 'default'\n  @Input('class') className?: string\n\n  readonly index = computed(() => (this.timeline ? this.timeline.items().indexOf(this) : 0))\n  readonly isLast = computed(() => (this.timeline ? this.index() === this.timeline.items().length - 1 : false))\n\n  constructor() {\n    if (!this.timeline) return\n    this.timeline.register(this)\n    inject(DestroyRef).onDestroy(() => this.timeline!.unregister(this))\n  }\n\n  get isFirst(): boolean {\n    return this.index() === 0\n  }\n\n  get direction(): TimelineDirection {\n    return this.timeline?.direction ?? 'vertical'\n  }\n\n  get density(): TimelineDensity {\n    return this.timeline?.density ?? 'default'\n  }\n\n  get effectiveSide(): TimelineSide {\n    if (this.side) return this.side\n    const t = this.timeline\n    if (!t) return 'left'\n    if (t.align === 'center') {\n      const even = this.index() % 2 === 0\n      if (t.direction === 'vertical') return even ? 'left' : 'right'\n      return even ? 'top' : 'bottom'\n    }\n    return t.resolvedSide\n  }\n\n  /** Render props (React `TimelineItemRenderProps`). */\n  get renderProps(): TimelineItemRenderProps {\n    return { index: this.index(), isLast: this.isLast(), side: this.effectiveSide, status: this.status }\n  }\n\n  get stagger(): string {\n    return `${Math.min(this.index(), 12) * 55}ms`\n  }\n\n  get hostClass(): string {\n    const direction = this.direction\n    const isCenter = this.timeline?.align === 'center'\n    const side = this.effectiveSide\n    const last = this.isLast()\n    const verticalSpacing = last ? '' : VERTICAL_SPACING[this.density]\n    const horizontalSpacing = last ? '' : HORIZONTAL_SPACING[this.density]\n    return cn(\n      'timeline-item-enter relative',\n      this.status === 'current' && 'timeline-item-current',\n      !isCenter &&\n        direction === 'vertical' &&\n        cn('flex gap-4', side === 'right' && 'flex-row-reverse text-right', verticalSpacing),\n      !isCenter &&\n        direction === 'horizontal' &&\n        cn('flex flex-col gap-2', side === 'bottom' && 'flex-col-reverse', horizontalSpacing),\n      isCenter &&\n        direction === 'vertical' &&\n        cn(\n          'grid grid-cols-[1fr_auto_1fr] items-start gap-x-4',\n          '[&>[data-slot=timeline-media]]:col-start-2 [&>[data-slot=timeline-media]]:row-start-1',\n          '[&>[data-slot=timeline-separator]]:col-start-2 [&>[data-slot=timeline-separator]]:row-start-1',\n          '[&>[data-slot=timeline-content]]:row-start-1',\n          side === 'left' && '[&>[data-slot=timeline-content]]:col-start-1 [&>[data-slot=timeline-content]]:text-right',\n          side === 'right' && '[&>[data-slot=timeline-content]]:col-start-3',\n          verticalSpacing,\n        ),\n      isCenter &&\n        direction === 'horizontal' &&\n        cn(\n          'grid grid-rows-[1fr_auto_1fr] items-start gap-y-2',\n          '[&>[data-slot=timeline-media]]:col-start-1 [&>[data-slot=timeline-media]]:row-start-2',\n          '[&>[data-slot=timeline-separator]]:col-start-1 [&>[data-slot=timeline-separator]]:row-start-2',\n          '[&>[data-slot=timeline-content]]:col-start-1',\n          side === 'top' && '[&>[data-slot=timeline-content]]:row-start-1 [&>[data-slot=timeline-content]]:self-end',\n          side === 'bottom' && '[&>[data-slot=timeline-content]]:row-start-3',\n          horizontalSpacing,\n        ),\n      this.className,\n    )\n  }\n}\n\nconst COLORED_CONNECTOR: Record<TimelineStatus, string> = {\n  default: 'bg-primary',\n  current: 'bg-primary',\n  success: 'bg-success',\n  warning: 'bg-warning',\n  error: 'bg-destructive',\n  info: 'bg-info',\n  muted: 'bg-muted-foreground/40',\n}\n\n/**\n * React `TimelineMedia`: the marker (dot / icon / avatar / outline, tinted by its own `status`\n * or the item's) plus the connector line to the next item, hidden on the last item or with\n * `hideConnector`. `coloredConnector` tints the line by status; `lineStyle` dashes / dots it.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-media, [ui-timeline-media]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-media\"',\n    '[attr.data-variant]': 'variant',\n    '[class]': 'hostClass',\n  },\n  template: `<div data-uipkge=\"\" data-slot=\"timeline-media-marker\" [class]=\"markerClass\"><ng-content /></div>\n    @if (showConnector) {\n      <div data-uipkge=\"\" data-slot=\"timeline-media-connector\" aria-hidden=\"true\" [class]=\"connectorClass\"></div>\n    }`,\n})\nexport class UiTimelineMediaComponent {\n  private readonly item = inject(UiTimelineItemComponent, { optional: true })\n\n  @Input() variant: TimelineMediaVariant = 'dot'\n  @Input() status?: TimelineStatus\n  /** Manually hide the auto-generated connector line. */\n  @Input({ transform: booleanAttribute }) hideConnector = false\n  /** Color the connector by the item's status instead of the neutral border. */\n  @Input({ transform: booleanAttribute }) coloredConnector = false\n  @Input() lineStyle: TimelineLineStyle = 'solid'\n  @Input('class') className?: string\n\n  get direction(): TimelineDirection {\n    return this.item?.direction ?? 'vertical'\n  }\n\n  get effectiveStatus(): TimelineStatus {\n    return this.status ?? this.item?.status ?? 'default'\n  }\n\n  get showConnector(): boolean {\n    return !this.hideConnector && !(this.item?.isLast() ?? true)\n  }\n\n  get hostClass(): string {\n    return cn(\n      'relative flex shrink-0 items-center',\n      this.direction === 'vertical' ? 'flex-col self-stretch' : 'flex-row items-center self-stretch',\n      this.className,\n    )\n  }\n\n  get markerClass(): string {\n    return cn(\n      timelineMediaVariants({ variant: this.variant, status: this.effectiveStatus }),\n      this.direction === 'vertical' && this.variant === 'dot' && 'mt-1',\n    )\n  }\n\n  private get connectorBgClass(): string {\n    const vertical = this.direction === 'vertical'\n    if (this.lineStyle === 'dashed') {\n      return vertical\n        ? 'border-l-2 border-dashed border-border bg-transparent w-0'\n        : 'border-t-2 border-dashed border-border bg-transparent h-0'\n    }\n    if (this.lineStyle === 'dotted') {\n      return vertical\n        ? 'border-l-2 border-dotted border-border bg-transparent w-0'\n        : 'border-t-2 border-dotted border-border bg-transparent h-0'\n    }\n    return this.coloredConnector ? COLORED_CONNECTOR[this.effectiveStatus] : 'bg-border'\n  }\n\n  get connectorClass(): string {\n    return cn(this.direction === 'vertical' ? 'my-1 w-px flex-1' : 'mx-1 h-px flex-1', this.connectorBgClass)\n  }\n}\n\n/**\n * React `TimelineSeparator`: a primary ring marker with an inner dot plus the connector line.\n * Replace the inner dot (React `dot` prop) by projecting an element with `slot=\"dot\"`.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-separator, [ui-timeline-separator]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-separator\"',\n    '[class]': 'hostClass',\n  },\n  template: `<div\n      data-slot=\"timeline-separator-marker\"\n      class=\"bg-primary ring-background relative z-10 flex size-4 items-center justify-center rounded-full shadow-2xs ring-4\"\n    >\n      <ng-content select=\"[slot=dot]\"><div class=\"bg-primary-foreground size-1.5 rounded-full\"></div></ng-content>\n    </div>\n    @if (showConnector) {\n      <div aria-hidden=\"true\" data-slot=\"timeline-media-connector\" [class]=\"connectorClass\"></div>\n    }`,\n})\nexport class UiTimelineSeparatorComponent {\n  private readonly item = inject(UiTimelineItemComponent, { optional: true })\n\n  /** Manually hide the auto-generated connector line. */\n  @Input({ transform: booleanAttribute }) hideConnector = false\n  @Input('class') className?: string\n\n  get direction(): TimelineDirection {\n    return this.item?.direction ?? 'vertical'\n  }\n\n  get showConnector(): boolean {\n    return !this.hideConnector && !(this.item?.isLast() ?? true)\n  }\n\n  get hostClass(): string {\n    return cn(\n      'relative flex shrink-0 items-center',\n      this.direction === 'vertical' ? 'w-4 flex-col self-stretch' : 'h-4 flex-row items-center self-stretch',\n      this.className,\n    )\n  }\n\n  get connectorClass(): string {\n    return cn('bg-border', this.direction === 'vertical' ? 'my-1.5 w-0.5 flex-1' : 'mx-1.5 h-0.5 flex-1')\n  }\n}\n\n/** React `TimelineContent` (a `<div>`). */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-content, [ui-timeline-content]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-content\"',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineContentComponent {\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn('block flex-1 space-y-1', this.className)\n  }\n}\n\n/** React `TimelineHeader` (a flex row: title + date / badge). */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-header, [ui-timeline-header]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-header\"',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineHeaderComponent {\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn('flex flex-wrap items-center justify-between gap-2', this.className)\n  }\n}\n\n/**\n * React `TimelineTitle` renders an `<h3>` (or the `as` element). Put it on a heading\n * (`<h3 ui-timeline-title>`) for the native element; the `<ui-timeline-title>` custom element\n * gets heading semantics via role=\"heading\" + aria-level from `as`.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-title, [ui-timeline-title]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-title\"',\n    '[attr.role]': 'ariaLevel ? \"heading\" : null',\n    '[attr.aria-level]': 'ariaLevel',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineTitleComponent {\n  private readonly isCustom = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement.tagName === 'UI-TIMELINE-TITLE'\n  @Input() as: TimelineTitleAs = 'h3'\n  @Input('class') className?: string\n\n  get ariaLevel(): number | null {\n    return this.isCustom && this.as !== 'div' ? Number(this.as.slice(1)) : null\n  }\n\n  get hostClass(): string {\n    return cn('block text-sm leading-none font-semibold tracking-tight', this.className)\n  }\n}\n\n/** React `TimelineDescription` (a `<p>`). */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-description, [ui-timeline-description]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-description\"',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineDescriptionComponent {\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn('block text-muted-foreground text-sm', this.className)\n  }\n}\n\n/** React `TimelineDate` (an inline `<time>`; use `<time ui-timeline-date>` for the native element). */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timeline-date, [ui-timeline-date]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"timeline-date\"',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiTimelineDateComponent {\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn('text-muted-foreground text-xs', this.className)\n  }\n}\n\nexport { timelineMediaVariants, type TimelineMediaVariant, type TimelineMediaVariantsProps }\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/timeline/timeline.component.ts"
    },
    {
      "path": "packages/registry-angular/components/timeline/timeline.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`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const timelineMediaVariants = cva(\n  'relative z-10 flex shrink-0 items-center justify-center rounded-full ring-4 ring-background transition-colors [&>svg]:shrink-0',\n  {\n    variants: {\n      variant: {\n        dot: 'size-2.5',\n        icon: 'size-8 border border-border bg-card text-muted-foreground shadow-2xs [&>svg]:size-4',\n        avatar:\n          'size-8 ring-2 ring-border bg-muted overflow-hidden [&>img]:size-full [&>img]:object-cover [&>[data-slot=avatar]]:size-full',\n        outline: 'size-8 border border-border bg-background text-foreground [&>svg]:size-4',\n      },\n      status: {\n        default: '',\n        current: '',\n        success: '',\n        warning: '',\n        error: '',\n        info: '',\n        muted: '',\n      },\n    },\n    compoundVariants: [\n      // DOT\n      {\n        variant: 'dot',\n        status: 'default',\n        class: 'bg-primary text-primary-foreground',\n      },\n      {\n        variant: 'dot',\n        status: 'current',\n        class: 'bg-primary text-primary-foreground ring-4 ring-primary/25',\n      },\n      {\n        variant: 'dot',\n        status: 'success',\n        class: 'bg-success text-success-foreground',\n      },\n      {\n        variant: 'dot',\n        status: 'warning',\n        class: 'bg-warning text-warning-foreground',\n      },\n      {\n        variant: 'dot',\n        status: 'error',\n        class: 'bg-destructive text-destructive-foreground',\n      },\n      {\n        variant: 'dot',\n        status: 'info',\n        class: 'bg-info text-info-foreground',\n      },\n      {\n        variant: 'dot',\n        status: 'muted',\n        class: 'bg-muted-foreground/30 text-muted-foreground',\n      },\n\n      // ICON\n      {\n        variant: 'icon',\n        status: 'default',\n        class: 'border-border bg-card text-muted-foreground',\n      },\n      {\n        variant: 'icon',\n        status: 'current',\n        class: 'border-primary/50 bg-primary/10 text-primary ring-4 ring-primary/15',\n      },\n      {\n        variant: 'icon',\n        status: 'success',\n        class: 'border-success/40 bg-success/10 text-success ring-4 ring-success/10',\n      },\n      {\n        variant: 'icon',\n        status: 'warning',\n        class: 'border-warning/40 bg-warning/10 text-warning ring-4 ring-warning/10',\n      },\n      {\n        variant: 'icon',\n        status: 'error',\n        class: 'border-destructive/40 bg-destructive/10 text-destructive ring-4 ring-destructive/10',\n      },\n      {\n        variant: 'icon',\n        status: 'info',\n        class: 'border-info/40 bg-info/10 text-info ring-4 ring-info/10',\n      },\n      {\n        variant: 'icon',\n        status: 'muted',\n        class: 'border-border/60 bg-muted/40 text-muted-foreground/60',\n      },\n\n      // OUTLINE\n      {\n        variant: 'outline',\n        status: 'default',\n        class: 'border-border text-foreground bg-background',\n      },\n      {\n        variant: 'outline',\n        status: 'current',\n        class: 'border-primary text-primary bg-background ring-4 ring-primary/25',\n      },\n      {\n        variant: 'outline',\n        status: 'success',\n        class: 'border-success text-success bg-background',\n      },\n      {\n        variant: 'outline',\n        status: 'warning',\n        class: 'border-warning text-warning bg-background',\n      },\n      {\n        variant: 'outline',\n        status: 'error',\n        class: 'border-destructive text-destructive bg-background',\n      },\n      {\n        variant: 'outline',\n        status: 'info',\n        class: 'border-info text-info bg-background',\n      },\n      {\n        variant: 'outline',\n        status: 'muted',\n        class: 'border-border text-muted-foreground bg-background',\n      },\n    ],\n    defaultVariants: {\n      variant: 'dot',\n      status: 'default',\n    },\n  },\n)\n\nexport type TimelineMediaVariantsProps = VariantProps<typeof timelineMediaVariants>\nexport type TimelineMediaVariant = 'dot' | 'icon' | 'avatar' | 'outline'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/timeline/timeline.variants.ts"
    },
    {
      "path": "packages/registry-angular/components/timeline/index.ts",
      "content": "export {\n  UiTimelineComponent,\n  UiTimelineItemComponent,\n  UiTimelineMediaComponent,\n  UiTimelineSeparatorComponent,\n  UiTimelineContentComponent,\n  UiTimelineHeaderComponent,\n  UiTimelineTitleComponent,\n  UiTimelineDescriptionComponent,\n  UiTimelineDateComponent,\n  timelineMediaVariants,\n  type TimelineMediaVariant,\n  type TimelineMediaVariantsProps,\n  type TimelineDirection,\n  type TimelineAlign,\n  type TimelineSide,\n  type TimelineStatus,\n  type TimelineDensity,\n  type TimelineMediaStatus,\n  type TimelineLineStyle,\n  type TimelineTitleAs,\n  type TimelineItemRenderProps,\n} from './timeline.component'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/timeline/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Vertical or horizontal sequence of events with auto-indexed items, dot / icon / avatar / outline markers and connector lines (none after the last item). Statuses (default, current, success, warning, error, info, muted) tint the marker and optionally the connector; align=center alternates sides. Use for activity feeds, audit logs, and progress tracking.",
  "categories": [
    "data-display"
  ]
}