{"$schema":"https://ui.shadcn.com/schema/registry.json","name":"easeui","homepage":"https://easeui.dev","items":[{"name":"accordion","type":"registry:component","title":"Accordion","description":"Expanding sections that grow to their natural height with no measuring, with arrow key navigation and closed panels skipped by Tab.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/accordion.tsx","type":"registry:component","target":"@components/motion/accordion.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"alert","type":"registry:component","title":"Alert","description":"Banner with a colored accent stripe. Body text stays neutral so contrast never depends on the variant.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/alert.tsx","type":"registry:component","target":"@components/motion/alert.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"avatar","type":"registry:component","title":"Avatar","description":"Round avatar in three sizes with an optional status dot. The image fades in on load and falls back to initials, and a group stacks them and fans them out on hover.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/avatar.tsx","type":"registry:component","target":"@components/motion/avatar.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"badge","type":"registry:component","title":"Badge","description":"Small status pill in five colors, with a crossfade for when its variant changes.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/badge.tsx","type":"registry:component","target":"@components/motion/badge.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"button","type":"registry:component","title":"Button","description":"Button with a quick press, hairline rings instead of borders, four variants, and a 44px tap area even at small sizes.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/button.tsx","type":"registry:component","target":"@components/motion/button.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"card","type":"registry:component","title":"Card","description":"Bordered surface with header, content, and footer parts for grouping related content.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/card.tsx","type":"registry:component","target":"@components/motion/card.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"checkbox","type":"registry:component","title":"Checkbox","description":"Checkbox on a real checkbox input, with a check mark that pops in rather than just appearing.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/checkbox.tsx","type":"registry:component","target":"@components/motion/checkbox.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"command-palette","type":"registry:component","title":"Command Palette","description":"Command-K style search overlay that filters as you type, with arrow keys to move the highlight and Enter to select.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/command-palette.tsx","type":"registry:component","target":"@components/motion/command-palette.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"copy-button","type":"registry:component","title":"Copy Button","description":"Copies text to the clipboard and trades its icon for a check for a moment, with a label or as a single icon.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/copy-button.tsx","type":"registry:component","target":"@components/motion/copy-button.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"diff-view","type":"registry:component","title":"Diff View","description":"A line-by-line diff with an added or removed mark in the gutter and a tinted row for each side. Renders a diff, doesn't compute one.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/diff-view.tsx","type":"registry:component","target":"@components/motion/diff-view.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"drawer","type":"registry:component","title":"Drawer","description":"Sheet that slides up from the bottom edge on the native dialog element. Drag the handle down, or flick it, to dismiss.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/drawer.tsx","type":"registry:component","target":"@components/motion/drawer.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"dropdown-menu","type":"registry:component","title":"Dropdown Menu","description":"Action menu that opens from a trigger, flips upward when there is no room below, and supports a destructive item.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/dropdown-menu.tsx","type":"registry:component","target":"@components/motion/dropdown-menu.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"dynamic-island","type":"registry:component","title":"Dynamic Island","description":"A pill that morphs between a compact status line and any number of named live-activity views, the same element reshaping via layout animation each time.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/dynamic-island.tsx","type":"registry:component","target":"@components/motion/dynamic-island.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"expandable-tabs","type":"registry:component","title":"Expandable Tabs","description":"A row of icon tabs where the selected one expands to reveal its label, sliding a shared background pill to match.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/expandable-tabs.tsx","type":"registry:component","target":"@components/motion/expandable-tabs.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"file-upload","type":"registry:component","title":"File Upload","description":"Dropzone that also opens the native file picker, with a list below it that pops each file in and collapses smoothly on remove.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/file-upload.tsx","type":"registry:component","target":"@components/motion/file-upload.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"gradient-text","type":"registry:component","title":"Gradient Text","description":"Text filled with a slowly drifting rainbow. It pauses while off screen and holds still when reduced motion is on.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/gradient-text.tsx","type":"registry:component","target":"@components/motion/gradient-text.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"hold-to-confirm","type":"registry:component","title":"Hold to Confirm","description":"Button for destructive actions that fills while you hold it and only fires once the fill completes. Letting go early drains it back quickly.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/hold-to-confirm.tsx","type":"registry:component","target":"@components/motion/hold-to-confirm.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"input","type":"registry:component","title":"Input","description":"Text input whose focus ring grows from a hairline to two px, with a destructive ring for aria-invalid.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/input.tsx","type":"registry:component","target":"@components/motion/input.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"insight-card","type":"registry:component","title":"Insight Card","description":"A stat tile: a label, a headline value, and an optional trend pill. Pass a Number Ticker as the value to have it roll in on change.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/insight-card.tsx","type":"registry:component","target":"@components/motion/insight-card.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"modal","type":"registry:component","title":"Modal","description":"Centered dialog on the native dialog element that fades and scales in, leaves faster than it arrives, and keeps focus inside while open.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/modal.tsx","type":"registry:component","target":"@components/motion/modal.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"morphing-search","type":"registry:component","title":"Morphing Search","description":"A circular search button that morphs into a text field: the same element grows and reshapes via a layout animation, rather than a new one popping in beside it.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/morphing-search.tsx","type":"registry:component","target":"@components/motion/morphing-search.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"notification-stack","type":"registry:component","title":"Notification Stack","description":"A deck of notification cards: collapsed to the top card with peeking edges behind it, fanning into a readable list on hover or focus.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/notification-stack.tsx","type":"registry:component","target":"@components/motion/notification-stack.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"number-ticker","type":"registry:component","title":"Number Ticker","description":"Number that rolls to a new value like an odometer, one changed digit at a time.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/number-ticker.tsx","type":"registry:component","target":"@components/motion/number-ticker.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"otp-input","type":"registry:component","title":"OTP Input","description":"One box per digit for a verification code, with paste and SMS autofill support and a shake for a wrong code.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/otp-input.tsx","type":"registry:component","target":"@components/motion/otp-input.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"popover","type":"registry:component","title":"Popover","description":"A panel of arbitrary content anchored to a trigger, opening on click and flipping above it when there's no room below.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/popover.tsx","type":"registry:component","target":"@components/motion/popover.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"progress","type":"registry:component","title":"Progress","description":"Progress bar that fills with scale instead of width, plus an indeterminate state for unknown durations.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/progress.tsx","type":"registry:component","target":"@components/motion/progress.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"pull-to-refresh","type":"registry:component","title":"Pull to Refresh","description":"A pull-down gesture over scrollable content: the indicator tracks the finger 1:1, then resists past the trigger distance and spins while refreshing.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/pull-to-refresh.tsx","type":"registry:component","target":"@components/motion/pull-to-refresh.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"range-slider","type":"registry:component","title":"Range Slider","description":"Native range input with a filled track and tick dots, so keyboard and screen reader support come built in.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/range-slider.tsx","type":"registry:component","target":"@components/motion/range-slider.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"rating","type":"registry:component","title":"Rating","description":"A row of stars that previews the score on hover, fills up to the pick in a quick wave, and moves one star at a time with the arrow keys.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/rating.tsx","type":"registry:component","target":"@components/motion/rating.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"select","type":"registry:component","title":"Select","description":"Composable select whose menu fades and scales out of the trigger, and opens upward when there is no room below.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/select.tsx","type":"registry:component","target":"@components/motion/select.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"selection-actions","type":"registry:component","title":"Selection Actions","description":"A floating bar of bulk actions that fades and rises in once a selection leaves zero, for pairing with Table or any selectable list.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/selection-actions.tsx","type":"registry:component","target":"@components/motion/selection-actions.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"skeleton","type":"registry:component","title":"Skeleton","description":"Loading placeholder with one shimmer shared across the page. Wrap real content and it takes the same shape, then crossfades away.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/skeleton.tsx","type":"registry:component","target":"@components/motion/skeleton.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"stepper","type":"registry:component","title":"Stepper","description":"Numbered steps joined by a line that fills as you move forward. Finished steps trade their number for a check and can be clicked to go back.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/stepper.tsx","type":"registry:component","target":"@components/motion/stepper.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"switch","type":"registry:component","title":"Switch","description":"On and off switch with a quick slide and no bounce. Inside a label, the whole row is the tap target.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/switch.tsx","type":"registry:component","target":"@components/motion/switch.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"table","type":"registry:component","title":"Table","description":"A plain, composable data table: sortable headers with an arrow that only shows on hover until active, and rows that tint when selected.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/table.tsx","type":"registry:component","target":"@components/motion/table.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"tabs","type":"registry:component","title":"Tabs","description":"Pill, segment, or underline tabs with an indicator that slides to the selected tab.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/tabs.tsx","type":"registry:component","target":"@components/motion/tabs.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"tag-input","type":"registry:component","title":"Tag Input","description":"A field that turns what you type into tags. Enter or a comma adds one, Backspace marks then removes the last, and a duplicate shakes the tag that already exists.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/tag-input.tsx","type":"registry:component","target":"@components/motion/tag-input.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"text-animation","type":"registry:component","title":"Text Animation","description":"One component, three ways to animate text: a scramble that resolves into place, a word or character reveal out of a blur, and a loading shimmer sweep.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/text-animation.tsx","type":"registry:component","target":"@components/motion/text-animation.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"textarea","type":"registry:component","title":"Textarea","description":"Textarea that grows with its content instead of scrolling, down to three rows and up to a scrollable cap.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/textarea.tsx","type":"registry:component","target":"@components/motion/textarea.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"theme-toggle","type":"registry:component","title":"Theme Toggle","description":"Light and dark switch that reveals the new theme as a circle growing from the center of the screen or from the button itself.","author":"Ryan","dependencies":["clsx","lucide-react","motion","next-themes","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/theme-toggle.tsx","type":"registry:component","target":"@components/motion/theme-toggle.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"toast","type":"registry:component","title":"Toast","description":"Toasts that stay long enough to read based on their word count, and pause while hovered or while the tab is in the background.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/toast.tsx","type":"registry:component","target":"@components/motion/toast.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"tooltip","type":"registry:component","title":"Tooltip","description":"Tooltip that shows a label with its keyboard shortcut, using Command on Mac and Ctrl elsewhere. Moving along a toolbar crossfades to the next tooltip with no delay.","author":"Ryan","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/tooltip.tsx","type":"registry:component","target":"@components/motion/tooltip.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"agent-loading-states","type":"registry:component","title":"Agent Loading States","description":"Three loading states for AI interfaces: shimmering status text, live agent progress, and cycling reasoning phrases.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/agent-loading-states.tsx","type":"registry:component","target":"@components/motion/agent-loading-states.tsx"},{"path":"components/motion/progress.tsx","type":"registry:component","target":"@components/motion/progress.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"code-block","type":"registry:component","title":"Code Block","description":"A code block with a label, a copy button, and light coloring for keywords, strings, comments and numbers, with no highlighter dependency.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/code-block.tsx","type":"registry:component","target":"@components/motion/code-block.tsx"},{"path":"components/motion/copy-button.tsx","type":"registry:component","target":"@components/motion/copy-button.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"flowchart","type":"registry:component","title":"Flowchart","description":"A sequence of steps on a dotted canvas, connected by curves that measure the actual cards and follow as you drag one. Click a step to light up its connectors, or edit an if/else step's chips with real dropdowns.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/flowchart.tsx","type":"registry:component","target":"@components/motion/flowchart.tsx"},{"path":"components/motion/badge.tsx","type":"registry:component","target":"@components/motion/badge.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"message-bubble","type":"registry:component","title":"Message Bubble","description":"A focused conversational surface with visual tones, independent alignment, and a speech-bubble tail that pops in on arrival.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/message-bubble.tsx","type":"registry:component","target":"@components/motion/message-bubble.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"pixel-loader","type":"registry:component","title":"Pixel Loader","description":"A 3x3 grid of cells that twinkle on independent, randomized cycles, paired with a shimmering label and an optional live elapsed timer.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/pixel-loader.tsx","type":"registry:component","target":"@components/motion/pixel-loader.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"prompt-input","type":"registry:component","title":"Prompt Input","description":"A chat composer that grows with its content, submits on Enter, and crossfades its send button into a stop button while a reply streams.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/prompt-input.tsx","type":"registry:component","target":"@components/motion/prompt-input.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"streaming-response","type":"registry:component","title":"Streaming Response","description":"Wraps a response with the actions people expect once it settles: copy, replay, share, a thumbs up or down, and a list of suggested follow-up prompts. No card or border, so the answer reads as part of the page.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/streaming-response.tsx","type":"registry:component","target":"@components/motion/streaming-response.tsx"},{"path":"components/motion/copy-button.tsx","type":"registry:component","target":"@components/motion/copy-button.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"streaming-text","type":"registry:component","title":"Streaming Text","description":"Reveals a model response as it streams in, fading each newly arrived word in on its own without replaying what's already on screen.","author":"Ryan","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/streaming-text.tsx","type":"registry:component","target":"@components/motion/streaming-text.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"todo-list","type":"registry:component","title":"Todo List","description":"A collapsible agent task plan with morphing status marks and a completion count.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/todo-list.tsx","type":"registry:component","target":"@components/motion/todo-list.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"tool-approval","type":"registry:component","title":"Tool Approval","description":"A card for an agent action waiting on approval, with Approve and Deny that collapse into a single status pill once resolved.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/tool-approval.tsx","type":"registry:component","target":"@components/motion/tool-approval.tsx"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]},{"name":"tool-chip","type":"registry:component","title":"Tool Chip","description":"A small pill naming a tool an agent used, with a spinner, check, or error mark that crossfades in place as its status changes.","author":"Ryan","dependencies":["clsx","lucide-react","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/tool-chip.tsx","type":"registry:component","target":"@components/motion/tool-chip.tsx"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts"}]}]}