{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"thinking-shimmer","type":"registry:component","title":"Agent Loading States Thinking Shimmer","description":"A quiet shimmer that keeps the agent's current status readable while work continues.","author":"Saurabh <saurabh10102@gmail.com>","dependencies":["clsx","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/agents/loading-states/thinking-shimmer.tsx","type":"registry:component","target":"@components/agents/loading-states/thinking-shimmer.tsx","content":"\"use client\";\n// beui.dev/components/agents/loading-states\n\nimport type { ReactNode } from \"react\";\nimport { TextShimmer } from \"@/components/motion/text-shimmer\";\nimport { useMessages } from \"@/lib/i18n\";\nimport { cn } from \"@/lib/utils\";\nimport { type LoadingStatesMessages, loadingStatesMessages } from \"./messages\";\n\nexport interface ThinkingShimmerProps {\n  /** Loading message shown to the user. */\n  children?: ReactNode;\n  /** Seconds taken for one shimmer pass. */\n  duration?: number;\n  /** Override any built-in string. */\n  labels?: Partial<LoadingStatesMessages>;\n  className?: string;\n}\n\nexport function ThinkingShimmer({\n  children,\n  duration = 1.8,\n  labels,\n  className,\n}: ThinkingShimmerProps) {\n  const t = useMessages(\"loadingStates\", loadingStatesMessages, labels);\n\n  return (\n    <TextShimmer\n      as=\"span\"\n      duration={duration}\n      className={cn(\"font-medium\", className)}\n    >\n      {children ?? t(\"thinking\")}\n    </TextShimmer>\n  );\n}\n"},{"path":"components/agents/loading-states/messages.ts","type":"registry:component","target":"@components/agents/loading-states/messages.ts","content":"/** English defaults. Override per instance with `labels`, or app-wide with `LocaleProvider`. */\nexport const loadingStatesMessages = {\n  thinking: \"Thinking…\",\n  churning: \"Churning\",\n  inProgress: \"{label}, in progress\",\n  reasoning: \"Reasoning\",\n  phraseThinking: \"Thinking\",\n  phraseReading: \"Reading the context\",\n  phraseConnecting: \"Connecting the details\",\n  phraseForming: \"Forming a response\",\n};\n\nexport type LoadingStatesMessages = typeof loadingStatesMessages;\n"},{"path":"components/motion/text-shimmer.tsx","type":"registry:component","target":"@components/motion/text-shimmer.tsx","content":"import { cn } from \"@/lib/utils\";\nimport type { ElementType, ReactNode } from \"react\";\nimport {\n  TEXT_SHIMMER_CLASS_NAME,\n  TEXT_SHIMMER_KEYFRAMES,\n  textShimmerStyle,\n} from \"@/lib/text-shimmer\";\n\nexport interface TextShimmerProps {\n  children: ReactNode;\n  as?: ElementType;\n  duration?: number;\n  className?: string;\n}\n\nexport function TextShimmer({ children, as: Comp = \"span\", duration = 2.5, className }: TextShimmerProps) {\n  return (\n    <>\n      <style>\n        {TEXT_SHIMMER_KEYFRAMES}\n      </style>\n      <Comp\n        style={textShimmerStyle(duration)}\n        className={cn(\n          \"inline-block\",\n          TEXT_SHIMMER_CLASS_NAME,\n          className,\n        )}\n      >\n        {children}\n      </Comp>\n    </>\n  );\n}\n"},{"path":"lib/i18n.tsx","type":"registry:lib","target":"@lib/i18n.tsx","content":"\"use client\";\n\nimport {\n  createContext,\n  type ReactNode,\n  useCallback,\n  useContext,\n  useMemo,\n} from \"react\";\n\n/**\n * A component's messages. Plain strings with `{name}` placeholders, so a\n * dictionary stays JSON-serializable and can be passed from a server layout.\n * There are no plural forms: write messages that read correctly for any count.\n */\nexport type Messages = Record<string, string>;\n\n/** A language file: component namespace → any subset of that component's messages. */\nexport type LocaleMessages = Record<string, Partial<Messages>>;\n\ntype LocaleContextValue = {\n  locale?: string;\n  messages: LocaleMessages;\n};\n\nconst NO_MESSAGES: LocaleMessages = {};\n\nconst LocaleContext = createContext<LocaleContextValue>({ messages: NO_MESSAGES });\n\n/** Without a locale, digits stay plain so server and client render the same text. */\nexport function formatNumber(value: number, locale?: string) {\n  return locale ? new Intl.NumberFormat(locale).format(value) : String(value);\n}\n\nexport interface LocaleProviderProps {\n  /** BCP 47 tag, e.g. `de`, `pt-BR`. Drives `Intl` number formatting. */\n  locale: string;\n  /**\n   * Component namespace → translated strings. Missing keys fall back to English.\n   * Pass a stable object (a module constant, imported JSON or a memoized value):\n   * a new object each render gives every translated component a new `t`.\n   */\n  messages?: LocaleMessages;\n  children: ReactNode;\n}\n\nexport function LocaleProvider({ locale, messages = NO_MESSAGES, children }: LocaleProviderProps) {\n  const value = useMemo(() => ({ locale, messages }), [locale, messages]);\n  return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>;\n}\n\nexport function useLocale(): { locale?: string } {\n  return { locale: useContext(LocaleContext).locale };\n}\n\n/**\n * Resolves a component's strings: `labels` prop, then the provider's\n * messages for `namespace`, then the component's English `defaults`.\n * Numbers passed as values are formatted for the provider's locale.\n */\nexport function useMessages<T extends Messages>(\n  namespace: string,\n  defaults: T,\n  labels?: Partial<T>,\n) {\n  const { locale, messages } = useContext(LocaleContext);\n  const provided = messages[namespace];\n  // Inline `labels={{ … }}` is a new object every render. Keying it by content\n  // keeps `t` one identity until a label changes, so effects and memos that\n  // depend on `t` don't re-run per render, and render-time reads never lag.\n  const labelsKey = labels ? JSON.stringify(labels) : \"\";\n  const stableLabels = useMemo(\n    () => (labelsKey ? (JSON.parse(labelsKey) as Partial<T>) : undefined),\n    [labelsKey],\n  );\n\n  return useCallback(\n    (key: keyof T & string, values?: Record<string, string | number>) => {\n      const template = stableLabels?.[key] ?? provided?.[key] ?? defaults[key];\n      if (!values) return template;\n      return template.replace(/\\{(\\w+)\\}/g, (match, name: string) => {\n        const value = values[name];\n        if (value === undefined) return match;\n        return typeof value === \"number\" ? formatNumber(value, locale) : value;\n      });\n    },\n    [defaults, stableLabels, locale, provided],\n  );\n}\n"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"},{"path":"lib/text-shimmer.ts","type":"registry:lib","target":"@lib/text-shimmer.ts","content":"import type { CSSProperties } from \"react\";\n\n// The reduced-motion rule travels with the component. The site's globals.css calms CSS\n// animation globally, but the registry bundles imports only, so an installed\n// copy never receives that reset.\n//\n// `!important` because the sweep is an inline style, which outranks a plain rule\n// in a media query. It selects a marker class carried by TEXT_SHIMMER_CLASS_NAME\n// so it also reaches consumers that build their own span out of these exports.\nexport const TEXT_SHIMMER_KEYFRAMES =\n  \"@keyframes beui-text-shimmer{from{background-position:200% 0}to{background-position:-200% 0}}\" +\n  \"@media (prefers-reduced-motion: reduce){.beui-text-shimmer{animation:none !important}}\";\n\nexport const TEXT_SHIMMER_CLASS_NAME =\n  \"beui-text-shimmer bg-[length:200%_100%] bg-clip-text text-transparent bg-[linear-gradient(110deg,var(--muted-foreground)_30%,var(--foreground)_50%,var(--muted-foreground)_70%)]\";\n\nexport function textShimmerStyle(duration: number): CSSProperties {\n  return {\n    animation: `beui-text-shimmer ${duration}s linear infinite`,\n  };\n}\n"}]}