{"$schema":"https://ui.shadcn.com/schema/registry.json","name":"fysk","homepage":"https://fysk.dev","items":[{"name":"avatar","type":"registry:ui","title":"Avatar","description":"An image element with a fallback for representing the user.","dependencies":["@radix-ui/react-avatar","class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json"],"files":[{"path":"fysk/avatar.tsx","type":"registry:ui"}]},{"name":"badge","type":"registry:ui","title":"Badge","description":"Displays a badge or a component that looks like a badge with mutliple variants.","dependencies":["class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/badge.tsx","type":"registry:ui"}]},{"name":"button","type":"registry:ui","title":"Button","description":"Standard button with multiple variants with built in state handling.","dependencies":["@radix-ui/react-slot","class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/button.tsx","type":"registry:ui"}]},{"name":"empty","type":"registry:ui","title":"Empty State","description":"A placeholder for when there is no data to display.","dependencies":["lucide-react","class-variance-authority"],"registryDependencies":["utils"],"files":[{"path":"fysk/empty.tsx","type":"registry:ui"}]},{"name":"form","type":"registry:ui","title":"Form","description":"Building forms with React Hook Form and Zod.","dependencies":["@radix-ui/react-label","@radix-ui/react-slot","lucide-react","react-hook-form"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json"],"files":[{"path":"fysk/form.tsx","type":"registry:ui"}]},{"name":"input","type":"registry:ui","title":"Input","description":"Displays a form input field that handles state for you.","dependencies":["class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/input.tsx","type":"registry:ui"}]},{"name":"input-otp","type":"registry:ui","title":"Input OTP","description":"Accessible one-time password component with copy paste functionality.","dependencies":[],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json"],"files":[{"path":"fysk/input-otp.tsx","type":"registry:ui"}]},{"name":"kbd","type":"registry:ui","title":"Kbd","description":"A Kbd component for keyboard key display, it auto-detects the command key (⌘ on Mac, Ctrl on Windows, etc.) and displays the appropriate symbol.","dependencies":["class-variance-authority"],"registryDependencies":["utils"],"files":[{"path":"fysk/kbd.tsx","type":"registry:ui"}]},{"name":"pagination","type":"registry:ui","title":"Pagination","description":"Pagination with page navigation, next and previous links.","dependencies":[],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/button.json"],"files":[{"path":"fysk/pagination.tsx","type":"registry:ui"}]},{"name":"progress","type":"registry:ui","title":"Progress component","description":"Progress component","dependencies":["@radix-ui/react-progress","class-variance-authority","lucide-react"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/progress.tsx","type":"registry:ui"}]},{"name":"select","type":"registry:ui","title":"Select","description":"Displays a list of options for the user to pick from—triggered by a button.","dependencies":["@radix-ui/react-select","@radix-ui/react-popover","class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/select.tsx","type":"registry:ui"}]},{"name":"switch","type":"registry:ui","title":"Switch","description":"A control that allows the user to toggle between checked and not checked.","dependencies":["@radix-ui/react-switch","class-variance-authority","lucide-react"],"registryDependencies":["utils","https://fysk.dev/r/fysk-provider.json","https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/switch.tsx","type":"registry:ui"}]},{"name":"tabs","type":"registry:ui","title":"Tabs","description":"A set of layered sections of content—known as tab panels—that are displayed one at a time.","dependencies":["@radix-ui/react-tabs"],"registryDependencies":["utils","https://fysk.dev/r/fysk-hooks.json","https://fysk.dev/r/fysk-provider.json"],"files":[{"path":"fysk/tabs.tsx","type":"registry:ui"}]},{"name":"textarea","type":"registry:ui","title":"Textarea","description":"An advance textarea component with auto-size and header/footer support, built on top of Radix UI.","dependencies":["class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/fysk-hooks.json","https://fysk.dev/r/fysk-provider.json"],"files":[{"path":"fysk/textarea.tsx","type":"registry:ui"}]},{"name":"tooltip","type":"registry:ui","title":"Tooltip","description":"A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.","dependencies":["@radix-ui/react-tooltip","class-variance-authority"],"registryDependencies":["utils","https://fysk.dev/r/kbd.json"],"files":[{"path":"fysk/tooltip.tsx","type":"registry:ui"}]},{"name":"typography","type":"registry:ui","title":"Typography","description":"Standard typography component.","dependencies":["class-variance-authority"],"registryDependencies":["utils"],"files":[{"path":"fysk/typography.tsx","type":"registry:ui"}]},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"fysk-provider","type":"registry:component","description":"Global provider for Fysk components.","dependencies":["lucide-react","framer-motion"],"registryDependencies":["https://fysk.dev/r/fysk-hooks.json"],"files":[{"path":"fysk/fysk-provider.tsx","type":"registry:component","content":"\"use client\"\r\nimport * as React from \"react\"\r\nimport { Check, AlertCircle, AlertTriangle, Info, ChevronDown, ChevronUp, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, MoreHorizontal, Search, Circle, Minus, PanelLeft, X, Loader2 } from \"lucide-react\"\r\nimport { motion, AnimatePresence } from \"framer-motion\"\r\n\r\nexport type FyskIconPosition = \"start\" | \"end\"\r\n\r\nexport interface FyskIcons {\r\n    loading?: React.ReactNode\r\n    success?: React.ReactNode\r\n    error?: React.ReactNode\r\n    warning?: React.ReactNode\r\n    info?: React.ReactNode,\r\n    chevronDown?: React.ReactNode,\r\n    chevronUp?: React.ReactNode,\r\n    chevronLeft?: React.ReactNode,\r\n    chevronRight?: React.ReactNode,\r\n    chevronsLeft?: React.ReactNode,\r\n    chevronsRight?: React.ReactNode,\r\n    moreHorizontal?: React.ReactNode,\r\n    search?: React.ReactNode,\r\n    circle?: React.ReactNode,\r\n    minus?: React.ReactNode,\r\n    panelLeft?: React.ReactNode,\r\n    close?: React.ReactNode,\r\n}\r\n\r\n// --- Animation Configuration ---\r\n\r\n/**\r\n * Easing types based on \"The Easing Blueprint\"\r\n * @see https://animations.dev/learn/animation-theory/the-easing-blueprint\r\n * \r\n * - easeOut: Best for enter animations (responsive feel)\r\n * - easeInOut: Best for layout/morphing (natural acceleration/deceleration)\r\n * - easeIn: Rarely used (feels sluggish)\r\n * - linear: Only for constant animations (marquees, progress)\r\n */\r\nexport type FyskEasing = \"easeOut\" | \"easeInOut\" | \"easeIn\" | \"linear\" | number[]\r\n\r\n/**\r\n * Duration presets in seconds\r\n */\r\nexport interface FyskDurations {\r\n    /** Ultra-fast micro-interactions (50-100ms) */\r\n    instant: number\r\n    /** Quick feedback animations (100-150ms) */\r\n    fast: number\r\n    /** Standard UI transitions (200-250ms) */\r\n    normal: number\r\n    /** Deliberate, noticeable animations (300-400ms) */\r\n    slow: number\r\n    /** Layout/morphing animations (400-500ms) */\r\n    layout: number\r\n}\r\n\r\n/**\r\n * Easing presets for different animation contexts\r\n */\r\nexport interface FyskEasings {\r\n    /** For elements entering the screen (dropdowns, modals, tooltips) */\r\n    enter: FyskEasing\r\n    /** For elements leaving the screen */\r\n    exit: FyskEasing\r\n    /** For elements morphing/changing size while on screen */\r\n    layout: FyskEasing\r\n    /** For hover/focus micro-interactions */\r\n    hover: FyskEasing\r\n    /** For constant animations (spinners, progress) */\r\n    linear: FyskEasing\r\n}\r\n\r\n/**\r\n * Animation effect configurations\r\n */\r\nexport interface FyskEffects {\r\n    /** Enable blur effect on enter/exit transitions */\r\n    blur: boolean\r\n    /** Blur amount in pixels */\r\n    blurAmount: number\r\n    /** Enable scale effect on enter/exit */\r\n    scale: boolean\r\n    /** Scale amount (0.95 = 95%) */\r\n    scaleAmount: number\r\n    /** Enable slide effect */\r\n    slide: boolean\r\n    /** Slide distance in pixels */\r\n    slideDistance: number\r\n}\r\n\r\n/**\r\n * Complete animation configuration\r\n */\r\nexport interface FyskAnimationConfig {\r\n    /** Master switch to enable/disable all animations */\r\n    enabled: boolean\r\n    /** The framer-motion 'motion' object. Type as any to avoid strict peerDependency if not used. */\r\n    motion?: any\r\n    /** The framer-motion 'AnimatePresence' component */\r\n    AnimatePresence?: React.ComponentType<any>\r\n    /** Duration presets (in seconds) */\r\n    durations: FyskDurations\r\n    /** Easing presets */\r\n    easings: FyskEasings\r\n    /** Visual effects */\r\n    effects: FyskEffects\r\n    /** Enable reduced motion for accessibility */\r\n    respectReducedMotion: boolean\r\n}\r\n\r\n/**\r\n * Default animation configuration - professionally tuned values\r\n */\r\nexport const defaultAnimationConfig: FyskAnimationConfig = {\r\n    motion: motion,\r\n    AnimatePresence: AnimatePresence,\r\n    enabled: true,\r\n    durations: {\r\n        instant: 0.1,\r\n        fast: 0.15,\r\n        normal: 0.2,\r\n        slow: 0.3,\r\n        layout: 0.4,\r\n    },\r\n    easings: {\r\n        enter: \"easeOut\",\r\n        exit: \"easeOut\",\r\n        layout: \"easeInOut\",\r\n        hover: \"easeOut\",\r\n        linear: \"linear\",\r\n    },\r\n    effects: {\r\n        blur: true,\r\n        blurAmount: 4,\r\n        scale: true,\r\n        scaleAmount: 0.95,\r\n        slide: true,\r\n        slideDistance: 10,\r\n    },\r\n    respectReducedMotion: true,\r\n}\r\n\r\nexport interface FyskConfig {\r\n    icons?: FyskIcons;\r\n    iconPosition?: FyskIconPosition;\r\n    animations?: Partial<FyskAnimationConfig>;\r\n}\r\n\r\nconst defaultIcons: Required<FyskIcons> = {\r\n    loading: <Loader2 className=\"animate-spin\" />,\r\n    success: <Check />,\r\n    error: <AlertCircle />,\r\n    warning: <AlertTriangle />,\r\n    info: <Info />,\r\n    chevronDown: <ChevronDown className=\"size-4\" />,\r\n    chevronUp: <ChevronUp className=\"size-4\" />,\r\n    chevronLeft: <ChevronLeft className=\"size-4\" />,\r\n    chevronRight: <ChevronRight className=\"size-4\" />,\r\n    chevronsLeft: <ChevronsLeft className=\"size-4\" />,\r\n    chevronsRight: <ChevronsRight className=\"size-4\" />,\r\n    moreHorizontal: <MoreHorizontal className=\"size-4\" />,\r\n    search: <Search className=\"size-4\" />,\r\n    circle: <Circle className=\"size-4\" />,\r\n    minus: <Minus className=\"size-4\" />,\r\n    panelLeft: <PanelLeft className=\"size-4\" />,\r\n    close: <X className=\"size-4\" />\r\n}\r\n\r\nconst FyskContext = React.createContext<FyskConfig>({\r\n    icons: defaultIcons,\r\n    iconPosition: \"start\",\r\n    animations: defaultAnimationConfig\r\n})\r\n\r\nexport function useFyskConfig() {\r\n    return React.useContext(FyskContext)\r\n}\r\n\r\nexport interface FyskProviderProps extends FyskConfig {\r\n    children: React.ReactNode\r\n}\r\n\r\nexport function FyskProvider({\r\n    children,\r\n    icons,\r\n    iconPosition = \"start\",\r\n    animations,\r\n}: FyskProviderProps) {\r\n    const value = React.useMemo(\r\n        () => ({\r\n            icons: { ...defaultIcons, ...icons },\r\n            iconPosition,\r\n            animations,\r\n        }),\r\n        [icons, iconPosition, animations]\r\n    )\r\n\r\n    return <FyskContext.Provider value={value}>{children}</FyskContext.Provider>\r\n}\r\n"}]},{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"fysk-hooks","type":"registry:hook","description":"Global hooks for Fysk components.","registryDependencies":["https://fysk.dev/r/fysk-provider.json"],"files":[{"path":"fysk/useFyskAnimation.ts","type":"registry:hook","content":"\"use client\";\r\nimport {\r\n  useFyskConfig,\r\n  defaultAnimationConfig,\r\n  type FyskAnimationConfig,\r\n} from \"@/components/fysk-provider\";\r\nimport React from \"react\";\r\n\r\n/**\r\n * Hook to access animation configuration and utilities.\r\n * Provides pre-computed transition objects for consistent animations across components.\r\n */\r\nexport function useFyskAnimation() {\r\n  const { animations } = useFyskConfig();\r\n\r\n  // Merge user config with defaults\r\n  const config: FyskAnimationConfig = React.useMemo(\r\n    () => ({\r\n      ...defaultAnimationConfig,\r\n      ...animations,\r\n      durations: {\r\n        ...defaultAnimationConfig.durations,\r\n        ...animations?.durations,\r\n      },\r\n      easings: { ...defaultAnimationConfig.easings, ...animations?.easings },\r\n      effects: { ...defaultAnimationConfig.effects, ...animations?.effects },\r\n    }),\r\n    [animations],\r\n  );\r\n\r\n  // Check if animations are enabled and motion is available\r\n  const isEnabled = config.enabled && !!config.motion;\r\n\r\n  // Pre-computed transition objects based on config\r\n  const transitions = React.useMemo(() => {\r\n    if (!isEnabled) return null;\r\n\r\n    const { durations, easings } = config;\r\n\r\n    return {\r\n      layout: {\r\n        duration: durations.layout,\r\n        ease: easings.layout,\r\n      },\r\n      enter: {\r\n        duration: durations.normal,\r\n        ease: easings.enter,\r\n      },\r\n      exit: {\r\n        duration: durations.fast,\r\n        ease: easings.exit,\r\n      },\r\n      hover: {\r\n        duration: durations.instant,\r\n        ease: easings.hover,\r\n      },\r\n      content: {\r\n        duration: durations.normal,\r\n        ease: easings.enter,\r\n        layout: {\r\n          duration: durations.slow,\r\n          ease: easings.layout,\r\n        },\r\n      },\r\n      linear: {\r\n        duration: durations.normal,\r\n        ease: easings.linear,\r\n        repeat: Infinity,\r\n      },\r\n      layoutEnter: {\r\n        layout: { duration: durations.layout, ease: easings.layout },\r\n        opacity: { duration: durations.normal, ease: easings.enter },\r\n      },\r\n    };\r\n  }, [isEnabled, config]);\r\n\r\n  // Pre-computed animation variants based on config\r\n  const variants = React.useMemo(() => {\r\n    if (!isEnabled) return null;\r\n\r\n    const { effects, durations, easings } = config;\r\n\r\n    const buildHiddenState = () => {\r\n      const state: Record<string, any> = { opacity: 0 };\r\n      if (effects.blur) state.filter = `blur(${effects.blurAmount}px)`;\r\n      if (effects.scale) state.scale = effects.scaleAmount;\r\n      if (effects.slide) state.y = effects.slideDistance;\r\n      return state;\r\n    };\r\n\r\n    const buildVisibleState = () => {\r\n      const state: Record<string, any> = { opacity: 1 };\r\n      if (effects.blur) state.filter = \"blur(0px)\";\r\n      if (effects.scale) state.scale = 1;\r\n      if (effects.slide) state.y = 0;\r\n      return state;\r\n    };\r\n\r\n    const buildExitState = () => {\r\n      const state: Record<string, any> = { opacity: 0 };\r\n      if (effects.blur) state.filter = `blur(${effects.blurAmount}px)`;\r\n      if (effects.scale) state.scale = effects.scaleAmount;\r\n      if (effects.slide) state.y = -effects.slideDistance;\r\n      return state;\r\n    };\r\n\r\n    return {\r\n      fadeSlide: {\r\n        initial: buildHiddenState(),\r\n        animate: buildVisibleState(),\r\n        exit: buildExitState(),\r\n        transition: { duration: durations.normal, ease: easings.enter },\r\n      },\r\n      fade: {\r\n        initial: { opacity: 0 },\r\n        animate: { opacity: 1 },\r\n        exit: { opacity: 0 },\r\n        transition: { duration: durations.fast, ease: easings.enter },\r\n      },\r\n      iconPop: {\r\n        initial: { scale: 0.5, rotate: -10 },\r\n        animate: { scale: 1, rotate: 0 },\r\n        transition: { duration: durations.normal, ease: easings.enter },\r\n      },\r\n      overlay: {\r\n        initial: { opacity: 0 },\r\n        animate: { opacity: 1 },\r\n        exit: { opacity: 0 },\r\n        transition: { duration: durations.fast, ease: easings.enter },\r\n      },\r\n      slideUp: {\r\n        initial: { opacity: 0, y: 20 },\r\n        animate: { opacity: 1, y: 0 },\r\n        exit: { opacity: 0, y: 10 },\r\n        transition: { duration: durations.normal, ease: easings.enter },\r\n      },\r\n      scaleIn: {\r\n        initial: { opacity: 0, scale: 0.95 },\r\n        animate: { opacity: 1, scale: 1 },\r\n        exit: { opacity: 0, scale: 0.95 },\r\n        transition: { duration: durations.normal, ease: easings.enter },\r\n      },\r\n    };\r\n  }, [isEnabled, config]);\r\n\r\n  const AnimatePresence = config.AnimatePresence || React.Fragment;\r\n\r\n  return {\r\n    isEnabled,\r\n    motion: isEnabled ? config.motion : null,\r\n    AnimatePresence,\r\n    config,\r\n    transitions,\r\n    variants,\r\n  };\r\n}\r\n"}]}]}