{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"faux-client","type":"registry:ui","title":"FauxClient","description":"Browser \/ device \/ window chrome rendering real, interactive UI inside (with scale-to-fit).","package":"react-fancy","dependencies":[],"registryDependencies":[],"files":[{"path":"components\/fancy\/faux-client\/FauxClient.tsx","content":"import { forwardRef, useLayoutEffect, useRef, useState } from \"react\";\r\nimport { cn } from \"..\/..\/utils\/cn\";\r\nimport type { FauxClientProps } from \".\/FauxClient.types\";\r\n\r\nconst DOT_COLORS = [\"bg-rose-300\", \"bg-amber-300\", \"bg-green-300\"];\r\n\r\n\/**\r\n * A frame that mimics a browser window or device, rendering **real UI** inside.\r\n * With a logical `width` + `scale=\"fit\"` it renders a full-size page\/app and\r\n * scales it down to fit any container (live thumbnails, device mockups) \u2014 the\r\n * content is real and interactive, just zoomed.\r\n *\r\n * Omit `width` for chrome around natural-size content (e.g. a code\/preview card).\r\n *\/\r\nexport const FauxClient = forwardRef<HTMLDivElement, FauxClientProps>(\r\n  (\r\n    {\r\n      variant = \"browser\",\r\n      url,\r\n      meta,\r\n      dots = true,\r\n      width,\r\n      scale = \"fit\",\r\n      className,\r\n      barClassName,\r\n      bodyClassName,\r\n      children,\r\n      ...rest\r\n    },\r\n    ref,\r\n  ) => {\r\n    const scaled = typeof width === \"number\";\r\n    const viewportRef = useRef<HTMLDivElement>(null);\r\n    const stageRef = useRef<HTMLDivElement>(null);\r\n    const [s, setS] = useState(typeof scale === \"number\" ? scale : 1);\r\n    const [contentHeight, setContentHeight] = useState<number | undefined>(undefined);\r\n\r\n    useLayoutEffect(() => {\r\n      if (!scaled) return;\r\n      const viewport = viewportRef.current;\r\n      const stage = stageRef.current;\r\n      if (!viewport || !stage) return;\r\n      if (typeof ResizeObserver === \"undefined\") return;\r\n\r\n      const measure = () => {\r\n        const avail = viewport.clientWidth;\r\n        const next = scale === \"fit\" ? (width && width > 0 ? avail \/ width : 1) : scale;\r\n        \/\/ offsetHeight is the pre-transform layout height (CSS transforms don't affect it).\r\n        const naturalHeight = stage.offsetHeight;\r\n        setS(next);\r\n        setContentHeight(naturalHeight * next);\r\n      };\r\n\r\n      measure();\r\n      const ro = new ResizeObserver(measure);\r\n      ro.observe(viewport);\r\n      ro.observe(stage);\r\n      return () => ro.disconnect();\r\n    }, [scaled, scale, width]);\r\n\r\n    const isBrowser = variant === \"browser\";\r\n    const isDevice = variant === \"device\";\r\n\r\n    return (\r\n      <div\r\n        ref={ref}\r\n        data-react-fancy-faux-client=\"\"\r\n        className={cn(\r\n          \"overflow-hidden bg-white dark:bg-zinc-900\",\r\n          isBrowser && \"rounded-2xl border border-zinc-200 shadow-xl dark:border-zinc-800\",\r\n          isDevice && \"rounded-[2rem] border-[10px] border-zinc-800 shadow-2xl dark:border-zinc-700\",\r\n          variant === \"bare\" && \"rounded-lg border border-zinc-200 dark:border-zinc-800\",\r\n          className,\r\n        )}\r\n        {...rest}\r\n      >\r\n        {isBrowser && (\r\n          <div\r\n            className={cn(\r\n              \"flex items-center gap-2 border-b border-zinc-200 bg-zinc-50 px-3.5 py-2.5 font-mono text-[11.5px] text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-400\",\r\n              barClassName,\r\n            )}\r\n          >\r\n            {dots && (\r\n              <div className=\"mr-1.5 flex gap-[5px]\">\r\n                {DOT_COLORS.map((color, i) => (\r\n                  <span key={i} className={cn(\"h-[9px] w-[9px] rounded-full\", color)} \/>\r\n                ))}\r\n              <\/div>\r\n            )}\r\n            {url != null && <span className=\"min-w-0 flex-1 truncate\">{url}<\/span>}\r\n            {meta != null && <span className=\"shrink-0 text-zinc-400 dark:text-zinc-500\">{meta}<\/span>}\r\n          <\/div>\r\n        )}\r\n\r\n        <div\r\n          ref={viewportRef}\r\n          className={cn(\"relative overflow-hidden\", bodyClassName)}\r\n          style={scaled ? { height: contentHeight } : undefined}\r\n        >\r\n          {scaled ? (\r\n            <div\r\n              ref={stageRef}\r\n              className=\"absolute left-0 top-0 origin-top-left\"\r\n              style={{ width, transform: `scale(${s})` }}\r\n            >\r\n              {children}\r\n            <\/div>\r\n          ) : (\r\n            children\r\n          )}\r\n        <\/div>\r\n      <\/div>\r\n    );\r\n  },\r\n);\r\n\r\nFauxClient.displayName = \"FauxClient\";\r\n","type":"registry:ui","target":"components\/fancy\/faux-client\/FauxClient.tsx"},{"path":"components\/fancy\/faux-client\/FauxClient.types.ts","content":"import type { HTMLAttributes, ReactNode } from \"react\";\r\n\r\nexport interface FauxClientProps extends Omit<HTMLAttributes<HTMLDivElement>, \"children\"> {\r\n  \/** Chrome style: a browser window, a device bezel, or no chrome. *\/\r\n  variant?: \"browser\" | \"device\" | \"bare\";\r\n  \/** Address-bar \/ title text (browser variant). *\/\r\n  url?: ReactNode;\r\n  \/** Right-aligned meta text in the bar (browser variant). *\/\r\n  meta?: ReactNode;\r\n  \/** Show the macOS traffic-light dots (browser variant). *\/\r\n  dots?: boolean;\r\n  \/**\r\n   * Logical content width in px. When set, children render at this width and are\r\n   * scaled to fit (see {@link FauxClientProps.scale}) \u2014 for downscaled previews\r\n   * of full pages\/apps. Omit to render children at natural size (chrome only).\r\n   *\/\r\n  width?: number;\r\n  \/** `\"fit\"` scales the logical {@link FauxClientProps.width} to the frame; a number is a fixed scale. *\/\r\n  scale?: number | \"fit\";\r\n  className?: string;\r\n  \/** Class for the browser bar. *\/\r\n  barClassName?: string;\r\n  \/** Class for the content viewport. *\/\r\n  bodyClassName?: string;\r\n  children?: ReactNode;\r\n}\r\n","type":"registry:ui","target":"components\/fancy\/faux-client\/FauxClient.types.ts"},{"path":"components\/fancy\/faux-client\/index.ts","content":"export { FauxClient } from \".\/FauxClient\";\r\nexport type { FauxClientProps } from \".\/FauxClient.types\";\r\n","type":"registry:ui","target":"components\/fancy\/faux-client\/index.ts"}]}