{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "terminal-animation",
  "type": "registry:ui",
  "description": "Composable terminal animation primitives with typed command playback, tabbed scenarios, and customizable output rendering",
  "dependencies": [
    "@radix-ui/react-slot",
    "@radix-ui/react-use-controllable-state"
  ],
  "files": [
    {
      "path": "registry/default/ui/terminal-animation.tsx",
      "content": "\"use client\"\n\nimport {\n  createContext,\n  useCallback,\n  useContext,\n  useEffect,\n  useRef,\n  useState,\n  type ReactNode,\n} from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\"\n\nimport { cn } from \"@/lib/utils\"\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TerminalLine {\n  text: string\n  color?: string\n  delay?: number\n}\n\nexport interface TabContent {\n  label: string\n  command: string\n  lines: TerminalLine[]\n}\n\nexport type TerminalAnimationRootProps = React.ComponentProps<\"div\"> & {\n  /** Tab content for each command */\n  tabs: TabContent[]\n  /** Initial active tab index (uncontrolled) */\n  defaultActiveTab?: number\n  /** Active tab index (controlled) */\n  activeTab?: number\n  /** Callback when active tab changes */\n  onActiveTabChange?: (index: number) => void\n  /** Optional background image URL; renders a full-bleed layer when provided */\n  backgroundImage?: string\n  /** Force dark mode for the terminal regardless of page theme */\n  alwaysDark?: boolean\n  /** Hide cursor after output completes */\n  hideCursorOnComplete?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\ninterface TerminalAnimationContextValue {\n  activeTab: number\n  setActiveTab: (index: number) => void\n  commandTyped: string\n  isTypingCommand: boolean\n  showCursor: boolean\n  visibleLines: number\n  currentTab: TabContent\n  tabs: TabContent[]\n}\n\nconst TerminalAnimationContext = createContext<\n  TerminalAnimationContextValue | undefined\n>(undefined)\n\nfunction useTerminalAnimationContext() {\n  const ctx = useContext(TerminalAnimationContext)\n  if (!ctx) {\n    throw new Error(\n      \"TerminalAnimation components must be used within TerminalAnimationRoot\"\n    )\n  }\n  return ctx\n}\n\n// ---------------------------------------------------------------------------\n// Default tabs data (for demo)\n// ---------------------------------------------------------------------------\n\nexport const defaultTerminalTabs: TabContent[] = [\n  {\n    label: \"new\",\n    command: \"vite new my-app\",\n    lines: [\n      { text: \"\", delay: 80 },\n      {\n        text: \"  Scaffolding project in ./my-app...\",\n        color: \"text-[#b39aff]\",\n        delay: 400,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  Select a framework:\", color: \"text-neutral-400\", delay: 200 },\n      { text: \"    Vanilla\", color: \"text-neutral-500\", delay: 80 },\n      { text: \"    Vue\", color: \"text-neutral-500\", delay: 80 },\n      { text: \"  > React\", color: \"text-[#32f3e9]\", delay: 80 },\n      { text: \"    Preact\", color: \"text-neutral-500\", delay: 80 },\n      { text: \"    Svelte\", color: \"text-neutral-500\", delay: 80 },\n      { text: \"\", delay: 200 },\n      { text: \"  Select a variant:\", color: \"text-neutral-400\", delay: 200 },\n      { text: \"  > TypeScript\", color: \"text-[#32f3e9]\", delay: 80 },\n      { text: \"\", delay: 200 },\n      { text: \"  Done. Now run:\", color: \"text-neutral-400\", delay: 300 },\n      { text: \"\", delay: 50 },\n      { text: \"    cd my-app\", color: \"text-neutral-300\", delay: 100 },\n      { text: \"    vite dev\", color: \"text-neutral-300\", delay: 100 },\n    ],\n  },\n  {\n    label: \"dev\",\n    command: \"vite dev\",\n    lines: [\n      { text: \"\", delay: 80 },\n      {\n        text: \"  VITE v6.3.0  ready in 124 ms\",\n        color: \"text-[#b39aff]\",\n        delay: 400,\n      },\n      { text: \"\", delay: 80 },\n      {\n        text: \"  >  Local:   http://localhost:5173/\",\n        color: \"text-[#32f3e9]\",\n        delay: 200,\n      },\n      {\n        text: \"  >  Network: http://192.168.1.42:5173/\",\n        color: \"text-neutral-500\",\n        delay: 100,\n      },\n      {\n        text: \"  >  press h + enter to show help\",\n        color: \"text-neutral-600\",\n        delay: 100,\n      },\n      { text: \"\", delay: 300 },\n      {\n        text: \"  hmr update /src/App.tsx 2ms\",\n        color: \"text-neutral-500\",\n        delay: 600,\n      },\n      {\n        text: \"  hmr update /src/App.tsx 1ms\",\n        color: \"text-neutral-500\",\n        delay: 400,\n      },\n    ],\n  },\n  {\n    label: \"lint\",\n    command: \"vite lint ./src\",\n    lines: [\n      { text: \"\", delay: 80 },\n      {\n        text: \"  Checked 42 files in 8ms\",\n        color: \"text-neutral-400\",\n        delay: 300,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  src/App.tsx:14:5\", color: \"text-neutral-500\", delay: 150 },\n      {\n        text: \"    warning  Unexpected console statement       no-console\",\n        color: \"text-yellow-400\",\n        delay: 100,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  src/utils.ts:31:1\", color: \"text-neutral-500\", delay: 150 },\n      {\n        text: \"    warning  Missing return type on function    return-type\",\n        color: \"text-yellow-400\",\n        delay: 100,\n      },\n      { text: \"\", delay: 80 },\n      {\n        text: \"  src/api/client.ts:7:10\",\n        color: \"text-neutral-500\",\n        delay: 150,\n      },\n      {\n        text: \"    error    Unused variable 'baseUrl'          no-unused\",\n        color: \"text-red-400\",\n        delay: 100,\n      },\n      { text: \"\", delay: 80 },\n      {\n        text: \"  3 problems (1 error, 2 warnings)  [8ms]\",\n        color: \"text-neutral-300\",\n        delay: 300,\n      },\n    ],\n  },\n  {\n    label: \"fmt\",\n    command: \"vite fmt ./src\",\n    lines: [\n      { text: \"\", delay: 80 },\n      {\n        text: \"  Checked 42 files in 4ms\",\n        color: \"text-neutral-400\",\n        delay: 200,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  Fixed  src/App.tsx\", color: \"text-[#22ff73]\", delay: 150 },\n      {\n        text: \"  Fixed  src/components/Header.tsx\",\n        color: \"text-[#22ff73]\",\n        delay: 100,\n      },\n      {\n        text: \"  Fixed  src/utils/format.ts\",\n        color: \"text-[#22ff73]\",\n        delay: 100,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  3 files formatted\", color: \"text-neutral-300\", delay: 200 },\n      { text: \"  39 files unchanged\", color: \"text-neutral-500\", delay: 100 },\n      { text: \"\", delay: 80 },\n      { text: \"  Done in 4ms\", color: \"text-neutral-400\", delay: 200 },\n    ],\n  },\n  {\n    label: \"test\",\n    command: \"vite test\",\n    lines: [\n      { text: \"\", delay: 80 },\n      { text: \"  RUN  v6.3.0\", color: \"text-[#b39aff]\", delay: 300 },\n      { text: \"\", delay: 80 },\n      { text: \"  src/utils.test.ts\", color: \"text-neutral-400\", delay: 200 },\n      {\n        text: \"    ✓ formats currency correctly\",\n        color: \"text-[#22ff73]\",\n        delay: 150,\n      },\n      {\n        text: \"    ✓ parses date strings\",\n        color: \"text-[#22ff73]\",\n        delay: 100,\n      },\n      {\n        text: \"    ✓ handles edge cases\",\n        color: \"text-[#22ff73]\",\n        delay: 100,\n      },\n      { text: \"  src/App.test.tsx\", color: \"text-neutral-400\", delay: 200 },\n      {\n        text: \"    ✓ renders heading\",\n        color: \"text-[#22ff73]\",\n        delay: 100,\n      },\n      {\n        text: \"    ✓ handles click events\",\n        color: \"text-[#22ff73]\",\n        delay: 100,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  Tests  5 passed (5)\", color: \"text-[#22ff73]\", delay: 200 },\n      { text: \"  Time   38ms\", color: \"text-neutral-500\", delay: 100 },\n    ],\n  },\n  {\n    label: \"build\",\n    command: \"vite build\",\n    lines: [\n      { text: \"\", delay: 80 },\n      {\n        text: \"  vite v6.3.0 building for production...\",\n        color: \"text-[#b39aff]\",\n        delay: 400,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  transforming...\", color: \"text-neutral-500\", delay: 300 },\n      {\n        text: \"  ✓ 42 modules transformed.\",\n        color: \"text-[#22ff73]\",\n        delay: 300,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  rendering chunks...\", color: \"text-neutral-500\", delay: 200 },\n      {\n        text: \"  computing gzip size...\",\n        color: \"text-neutral-500\",\n        delay: 200,\n      },\n      { text: \"\", delay: 80 },\n      {\n        text: \"  dist/index.html                 0.46 kB  |  gzip:  0.30 kB\",\n        color: \"text-neutral-400\",\n        delay: 100,\n      },\n      {\n        text: \"  dist/assets/index-Dk2a9f.css    1.28 kB  |  gzip:  0.65 kB\",\n        color: \"text-neutral-400\",\n        delay: 80,\n      },\n      {\n        text: \"  dist/assets/index-Ba3x7q.js   143.36 kB  |  gzip: 46.12 kB\",\n        color: \"text-neutral-400\",\n        delay: 80,\n      },\n      { text: \"\", delay: 80 },\n      { text: \"  ✓ built in 218ms\", color: \"text-[#22ff73]\", delay: 300 },\n    ],\n  },\n]\n\n// ---------------------------------------------------------------------------\n// Root\n// ---------------------------------------------------------------------------\n\nexport function TerminalAnimationRoot({\n  tabs,\n  defaultActiveTab = 0,\n  activeTab: activeTabProp,\n  onActiveTabChange,\n  backgroundImage,\n  alwaysDark = false,\n  hideCursorOnComplete = true,\n  className,\n  children,\n  ...props\n}: TerminalAnimationRootProps) {\n  const [activeTab, setActiveTab] = useControllableState({\n    prop: activeTabProp,\n    defaultProp: defaultActiveTab,\n    onChange: onActiveTabChange,\n  })\n\n  const [visibleLines, setVisibleLines] = useState(0)\n  const [commandTyped, setCommandTyped] = useState(\"\")\n  const [isTypingCommand, setIsTypingCommand] = useState(true)\n  const [showCursor, setShowCursor] = useState(true)\n  const timeoutRef = useRef<ReturnType<typeof setTimeout>[]>([])\n\n  const clearTimeouts = useCallback(() => {\n    timeoutRef.current.forEach(clearTimeout)\n    timeoutRef.current = []\n  }, [])\n\n  const animateTab = useCallback(\n    (tabIndex: number) => {\n      clearTimeouts()\n      setVisibleLines(0)\n      setCommandTyped(\"\")\n      setIsTypingCommand(true)\n      setShowCursor(true)\n\n      const tab = tabs[tabIndex]\n      if (!tab) {\n        return\n      }\n\n      const command = tab.command\n      let charIndex = 0\n\n      const typeCommand = () => {\n        if (charIndex <= command.length) {\n          setCommandTyped(command.slice(0, charIndex))\n          charIndex++\n          const t = setTimeout(typeCommand, 25 + Math.random() * 35)\n          timeoutRef.current.push(t)\n        } else {\n          const t = setTimeout(() => {\n            setIsTypingCommand(false)\n            showLines(0)\n          }, 250)\n          timeoutRef.current.push(t)\n        }\n      }\n\n      const showLines = (lineIndex: number) => {\n        if (lineIndex <= tab.lines.length) {\n          setVisibleLines(lineIndex)\n          if (lineIndex < tab.lines.length) {\n            const delay = tab.lines[lineIndex].delay ?? 100\n            const t = setTimeout(() => showLines(lineIndex + 1), delay)\n            timeoutRef.current.push(t)\n          } else if (hideCursorOnComplete) {\n            const t = setTimeout(() => setShowCursor(false), 600)\n            timeoutRef.current.push(t)\n          }\n        }\n      }\n\n      const t = setTimeout(typeCommand, 300)\n      timeoutRef.current.push(t)\n    },\n    [clearTimeouts, hideCursorOnComplete, tabs]\n  )\n\n  useEffect(() => {\n    animateTab(activeTab)\n    return clearTimeouts\n  }, [activeTab, animateTab, clearTimeouts])\n\n  const currentTab = tabs[activeTab] ?? tabs[0]\n  const safeActiveTab = Math.min(activeTab, tabs.length - 1)\n\n  const value: TerminalAnimationContextValue = {\n    activeTab: safeActiveTab,\n    setActiveTab,\n    commandTyped,\n    isTypingCommand,\n    showCursor,\n    visibleLines,\n    currentTab,\n    tabs,\n  }\n\n  return (\n    <TerminalAnimationContext.Provider value={value}>\n      <div\n        className={cn(alwaysDark && \"dark\", className)}\n        data-slot=\"terminal-animation-root\"\n        {...props}\n      >\n        {backgroundImage && (\n          <div\n            aria-hidden\n            className=\"absolute inset-0 bg-cover bg-center\"\n            data-slot=\"terminal-animation-background\"\n            style={{ backgroundImage: `url(${backgroundImage})` }}\n          />\n        )}\n        {children}\n      </div>\n    </TerminalAnimationContext.Provider>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// BackgroundGradient\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationBackgroundGradientProps =\n  React.ComponentProps<\"div\">\n\nconst backgroundGradientClasses =\n  \"absolute inset-0 bg-gradient-to-br from-violet-600/40 via-fuchsia-600/30 to-indigo-950\"\n\nexport function TerminalAnimationBackgroundGradient({\n  className,\n  ...props\n}: TerminalAnimationBackgroundGradientProps) {\n  return (\n    <div\n      aria-hidden\n      className={cn(backgroundGradientClasses, className)}\n      data-slot=\"terminal-animation-background-gradient\"\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Container\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationContainerProps = React.ComponentProps<\"div\">\n\nconst containerClasses =\n  \"relative w-full max-w-[62rem] px-3 pt-10 pb-0 md:px-0 md:pt-28\"\n\nexport function TerminalAnimationContainer({\n  className,\n  ...props\n}: TerminalAnimationContainerProps) {\n  return (\n    <div\n      className={cn(containerClasses, className)}\n      data-slot=\"terminal-animation-container\"\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Window\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationWindowProps = React.ComponentProps<\"div\"> & {\n  /** Terminal window background color; defaults to bg-card when unset */\n  backgroundColor?: string\n  /** Minimum height of the terminal window */\n  minHeight?: string\n  /** Animate slide-up when element enters viewport */\n  animateOnVisible?: boolean\n}\n\nconst windowClasses = \"relative flex flex-col overflow-hidden rounded-t-xl\"\n\nexport function TerminalAnimationWindow({\n  className,\n  backgroundColor,\n  minHeight = \"28rem\",\n  animateOnVisible = true,\n  style,\n  ...props\n}: TerminalAnimationWindowProps) {\n  const windowRef = useRef<HTMLDivElement>(null)\n  const [hasAnimated, setHasAnimated] = useState(false)\n\n  useEffect(() => {\n    if (!(animateOnVisible && windowRef.current)) {\n      return\n    }\n    const el = windowRef.current\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        if (entry?.isIntersecting) {\n          setHasAnimated(true)\n        }\n      },\n      { threshold: 0.1 }\n    )\n    observer.observe(el)\n    return () => observer.disconnect()\n  }, [animateOnVisible])\n\n  return (\n    <div\n      className={cn(\n        windowClasses,\n        !backgroundColor && \"bg-card\",\n        animateOnVisible &&\n          \"transition-transform duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)]\",\n        animateOnVisible && !hasAnimated && \"translate-y-64\",\n        animateOnVisible && hasAnimated && \"translate-y-0\",\n        className\n      )}\n      data-slot=\"terminal-animation-window\"\n      ref={windowRef}\n      style={\n        backgroundColor\n          ? { backgroundColor, minHeight, ...style }\n          : { minHeight, ...style }\n      }\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Content\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationContentProps = React.ComponentProps<\"div\">\n\nconst contentClasses = \"flex-1 px-6 py-6 sm:px-10 sm:py-8\"\n\nexport function TerminalAnimationContent({\n  className,\n  ...props\n}: TerminalAnimationContentProps) {\n  return (\n    <div\n      className={cn(contentClasses, className)}\n      data-slot=\"terminal-animation-content\"\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// BlinkingCursor\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationBlinkingCursorProps = React.ComponentProps<\"span\">\n\nexport function TerminalAnimationBlinkingCursor({\n  className,\n  ...props\n}: TerminalAnimationBlinkingCursorProps) {\n  // Requires `animate-caret-blink` utility in global CSS for downstream installs.\n  return (\n    <span\n      aria-hidden\n      className={cn(\n        \"animate-caret-blink bg-muted-foreground ml-0.5 inline-block h-[18px] w-[7px] translate-y-[3px] duration-1000\",\n        className\n      )}\n      data-slot=\"terminal-animation-blinking-cursor\"\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// CommandBar\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationCommandBarProps = React.ComponentProps<\"div\"> & {\n  /** Custom cursor element when typing; defaults to unstyled block cursor */\n  cursor?: ReactNode\n}\n\nexport function TerminalAnimationCommandBar({\n  className,\n  cursor,\n  ...props\n}: TerminalAnimationCommandBarProps) {\n  const { commandTyped, isTypingCommand, showCursor } =\n    useTerminalAnimationContext()\n\n  const defaultCursor = <span aria-hidden=\"true\">▌</span>\n\n  return (\n    <div\n      className={className}\n      data-slot=\"terminal-animation-command\"\n      {...props}\n    >\n      {commandTyped}\n      {isTypingCommand && showCursor && (cursor ?? defaultCursor)}\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// OutputLine\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationOutputLineProps = React.ComponentProps<\"div\"> & {\n  line: TerminalLine\n  visible: boolean\n}\n\nexport function TerminalAnimationOutputLine({\n  line,\n  visible,\n  className,\n  ...props\n}: TerminalAnimationOutputLineProps) {\n  if (!visible) {\n    return null\n  }\n  return (\n    <div\n      className={className}\n      data-slot=\"terminal-animation-output-line\"\n      {...props}\n    >\n      <span data-line-color={line.color}>{line.text || \"\\u00A0\"}</span>\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Output\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationOutputProps = React.ComponentProps<\"div\"> & {\n  /** Custom renderer for each line; receives line, index, and visibility */\n  renderLine?: (\n    line: TerminalLine,\n    index: number,\n    visible: boolean\n  ) => ReactNode\n}\n\nexport function TerminalAnimationOutput({\n  className,\n  renderLine,\n  ...props\n}: TerminalAnimationOutputProps) {\n  const { isTypingCommand, visibleLines, currentTab, activeTab } =\n    useTerminalAnimationContext()\n\n  if (isTypingCommand) {\n    return null\n  }\n\n  return (\n    <div\n      aria-atomic=\"false\"\n      aria-live=\"polite\"\n      className={className}\n      data-slot=\"terminal-animation-output\"\n      role=\"log\"\n      {...props}\n    >\n      {currentTab.lines.map((line, i) => {\n        const visible = i < visibleLines\n        const key = `${activeTab}-${i}`\n        if (renderLine) {\n          const content = renderLine(line, i, visible)\n          if (!(visible || content)) {\n            return null\n          }\n          return <div key={key}>{content}</div>\n        }\n        return (\n          <TerminalAnimationOutputLine\n            key={key}\n            line={line}\n            visible={visible}\n          />\n        )\n      })}\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// TrailingPrompt\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationTrailingPromptProps = React.ComponentProps<\"div\">\n\nexport function TerminalAnimationTrailingPrompt({\n  className,\n  children,\n  ...props\n}: TerminalAnimationTrailingPromptProps) {\n  const { isTypingCommand, showCursor, visibleLines, currentTab } =\n    useTerminalAnimationContext()\n\n  const show =\n    !isTypingCommand && showCursor && visibleLines >= currentTab.lines.length\n\n  if (!show) {\n    return null\n  }\n\n  return (\n    <div\n      className={className}\n      data-slot=\"terminal-animation-trailing-prompt\"\n      {...props}\n    >\n      {children}\n    </div>\n  )\n}\n\n// ---------------------------------------------------------------------------\n// TabList\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationTabListProps = React.ComponentProps<\"div\">\n\nexport function TerminalAnimationTabList({\n  className,\n  ...props\n}: TerminalAnimationTabListProps) {\n  return (\n    <div\n      aria-label=\"Terminal commands\"\n      className={className}\n      data-slot=\"terminal-animation-tab-list\"\n      role=\"tablist\"\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// TabTrigger\n// ---------------------------------------------------------------------------\n\nexport type TerminalAnimationTabTriggerProps =\n  React.ComponentPropsWithoutRef<\"button\"> & {\n    /** Tab index to activate when clicked */\n    index: number\n    /** Merge props onto child element instead of rendering a button */\n    asChild?: boolean\n  }\n\nexport function TerminalAnimationTabTrigger({\n  index,\n  asChild = false,\n  className,\n  children,\n  ...props\n}: TerminalAnimationTabTriggerProps) {\n  const { activeTab, setActiveTab } = useTerminalAnimationContext()\n  const isActive = activeTab === index\n\n  const triggerProps = {\n    role: \"tab\" as const,\n    \"aria-selected\": isActive,\n    \"data-state\": isActive ? \"active\" : \"inactive\",\n    onClick: () => setActiveTab(index),\n    children,\n  }\n\n  if (asChild) {\n    return <Slot {...triggerProps} {...props} className={className} />\n  }\n\n  return (\n    <button\n      data-slot=\"terminal-animation-tab-trigger\"\n      type=\"button\"\n      {...triggerProps}\n      className={className}\n      {...props}\n    />\n  )\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useTerminalAnimation() {\n  return useTerminalAnimationContext()\n}\n",
      "type": "registry:ui"
    }
  ]
}