{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "realmorphism-kit", "type": "registry:block", "title": "Realmorphism Kit", "description": "Full kit showroom with knobs, compact roller, text roller, and showroom figlet wheel.", "author": "LT Lo TeknowledG", "registryDependencies": [ "http://localhost:3050/registry/realmorphism.json", "http://localhost:3050/registry/realmorphism-base.json", "http://localhost:3050/registry/realmorphism-rolling-pickers.json" ], "dependencies": [ "@radix-ui/react-checkbox", "@radix-ui/react-label", "@radix-ui/react-switch", "@radix-ui/react-slot", "class-variance-authority", "clsx", "tailwind-merge" ], "files": [ { "type": "registry:component", "path": "checkbox.tsx", "target": "components/realmorphism/ui/checkbox.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport { Check } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Checkbox = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n \n \n));\nCheckbox.displayName = CheckboxPrimitive.Root.displayName;\n\nexport { Checkbox };\n" }, { "type": "registry:component", "path": "input.tsx", "target": "components/realmorphism/ui/input.tsx", "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Input = React.forwardRef>(\n ({ className, type, ...props }, ref) => {\n return (\n \n );\n },\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n" }, { "type": "registry:component", "path": "label.tsx", "target": "components/realmorphism/ui/label.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst labelVariants = cva(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nconst Label = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef & VariantProps\n>(({ className, ...props }, ref) => (\n \n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n" }, { "type": "registry:component", "path": "switch.tsx", "target": "components/realmorphism/ui/switch.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Switch = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n \n \n));\nSwitch.displayName = SwitchPrimitives.Root.displayName;\n\nexport { Switch };\n" }, { "type": "registry:component", "path": "toggle.tsx", "target": "components/realmorphism/ui/toggle.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst toggleBase =\n \"relative inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border text-sm font-medium outline-none disabled:pointer-events-none disabled:cursor-not-allowed before:pointer-events-auto before:absolute before:-inset-1 before:content-[''] before:bg-transparent [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\";\n\nconst toggleVariants = cva(toggleBase, {\n variants: {\n variant: {\n default:\n \"border-border bg-card text-card-foreground shadow-[2px_2px_0_1px_var(--button-wall)] transition-[color,box-shadow,transform] hover:-translate-x-[2px] hover:-translate-y-[2px] hover:shadow-[4px_4px_0_1px_var(--button-wall)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none data-[state=on]:bg-primary data-[state=on]:text-primary-foreground\",\n outline:\n \"border-border bg-background text-foreground shadow-[2px_2px_0_1px_var(--button-wall)] transition-[color,box-shadow,transform] hover:-translate-x-[2px] hover:-translate-y-[2px] hover:shadow-[4px_4px_0_1px_var(--button-wall)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none data-[state=on]:bg-primary data-[state=on]:text-primary-foreground\",\n secondary:\n \"border-border bg-secondary text-secondary-foreground shadow-[2px_2px_0_1px_var(--button-wall)] transition-[color,box-shadow,transform] hover:-translate-x-[2px] hover:-translate-y-[2px] hover:shadow-[4px_4px_0_1px_var(--button-wall)] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none data-[state=on]:bg-primary data-[state=on]:text-primary-foreground\",\n realmorphism:\n \"realmorphism-control font-mono tracking-[0.06em] transition-[transform,box-shadow,color,background-color,border-color] duration-150 ease-out\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 px-6 has-[>svg]:px-4\",\n operational: \"px-5 py-3\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n});\n\nexport interface ToggleProps\n extends\n Omit, \"onChange\">,\n VariantProps {\n pressed?: boolean;\n defaultPressed?: boolean;\n onPressedChange?: (pressed: boolean) => void;\n}\n\nfunction Toggle({\n className,\n variant,\n size,\n pressed,\n defaultPressed = false,\n onPressedChange,\n onClick,\n type = \"button\",\n ...props\n}: ToggleProps) {\n const [uncontrolledPressed, setUncontrolledPressed] = React.useState(defaultPressed);\n const isControlled = typeof pressed === \"boolean\";\n const isPressed = isControlled ? pressed : uncontrolledPressed;\n\n const handleClick = (event: React.MouseEvent) => {\n onClick?.(event);\n\n if (event.defaultPrevented) {\n return;\n }\n\n const nextPressed = !isPressed;\n\n if (!isControlled) {\n setUncontrolledPressed(nextPressed);\n }\n\n onPressedChange?.(nextPressed);\n };\n\n return (\n \n );\n}\n\nexport { Toggle, toggleVariants };\n" }, { "type": "registry:component", "path": "knob.tsx", "target": "components/realmorphism/ui/knob.tsx", "content": "\"use client\";\n\n/*\n * Copyright (c) 2026 by Dan (https://codepen.io/toomanyrequests/pen/BazyEEo)\n * Licensed under the MIT License.\n */\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\ntype KnobMode = \"power\" | \"balance\" | \"tuner\";\ntype KnobSize = \"sm\" | \"md\" | \"lg\";\n\ntype KnobProps = {\n label: string;\n unit?: string;\n min?: number;\n max?: number;\n step?: number;\n wheelMultiplier?: number;\n dragMultiplier?: number;\n value?: number;\n defaultValue?: number;\n onValueChange?: (value: number) => void;\n mode?: KnobMode;\n size?: KnobSize;\n theme?: \"light\" | \"dark\";\n className?: string;\n disabled?: boolean;\n active?: boolean;\n defaultActive?: boolean;\n onActiveChange?: (active: boolean) => void;\n clickTogglesActive?: boolean;\n};\n\nfunction clamp(value: number, min: number, max: number) {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction decimalPlaces(value: number) {\n const text = String(value);\n if (!text.includes(\".\")) {\n return 0;\n }\n\n return text.split(\".\")[1]?.length ?? 0;\n}\n\nfunction roundToStep(value: number, step: number, min: number) {\n const precision = Math.max(decimalPlaces(step), decimalPlaces(min));\n const next = Math.round((value - min) / step) * step + min;\n return Number(next.toFixed(precision));\n}\n\nfunction knobSizeClass(size: KnobSize) {\n switch (size) {\n case \"sm\":\n return \"w-16\";\n case \"lg\":\n return \"w-24\";\n case \"md\":\n default:\n return \"w-20\";\n }\n}\n\nexport function Knob({\n label,\n unit,\n min = 0,\n max = 100,\n step = 1,\n wheelMultiplier = 1,\n dragMultiplier = 1,\n value,\n defaultValue = min,\n onValueChange,\n mode = \"power\",\n size = \"md\",\n theme = \"dark\",\n className,\n disabled = false,\n active,\n defaultActive = true,\n onActiveChange,\n clickTogglesActive,\n}: KnobProps) {\n const inputRef = React.useRef(null);\n const hostRef = React.useRef(null);\n const knobRef = React.useRef(null);\n const dragPointerIdRef = React.useRef(null);\n const lastPointerXRef = React.useRef(null);\n const lastPointerYRef = React.useRef(null);\n const dragAccumulatorRef = React.useRef(0);\n const didDragRef = React.useRef(false);\n const activeAtPointerDownRef = React.useRef(defaultActive);\n const inputId = React.useId();\n const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);\n const [isDragging, setIsDragging] = React.useState(false);\n const [isHovered, setIsHovered] = React.useState(false);\n const isControlled = typeof value === \"number\";\n const currentValue = isControlled ? value : uncontrolledValue;\n const isActiveControlled = typeof active === \"boolean\";\n const [uncontrolledActive, setUncontrolledActive] = React.useState(defaultActive);\n const currentActive = isActiveControlled ? active : uncontrolledActive;\n const safeMin = Math.min(min, max);\n const safeMax = Math.max(min, max);\n const safeStep = step > 0 ? step : 1;\n const normalizedValue = clamp(currentValue, safeMin, safeMax);\n const span = Math.abs(safeMin) + Math.abs(safeMax) || 1;\n const rawPercent = (normalizedValue / span) * 100;\n const isBalance = mode === \"balance\";\n const isTuner = mode === \"tuner\";\n const isCenteredMode = isBalance || isTuner;\n const isLight = theme === \"light\";\n const balanceExtent = Math.max(Math.abs(safeMin), Math.abs(safeMax)) || 1;\n const balancePercent = clamp(Math.abs(normalizedValue) / balanceExtent, 0, 1);\n const hasBalanceHighlight = balancePercent > 0;\n const valueSweep = clamp((normalizedValue - safeMin) / (safeMax - safeMin || 1), 0, 1);\n const wheelStep = safeStep * 1.5 * Math.max(wheelMultiplier, 0.1);\n const formattedValue = isBalance\n ? normalizedValue === 0\n ? \"0\"\n : `+${Math.abs(normalizedValue)}`\n : isTuner\n ? normalizedValue === 0\n ? \"0\"\n : normalizedValue > 0\n ? `+${Math.abs(normalizedValue)}`\n : `${normalizedValue}`\n : `${normalizedValue}`;\n const formattedUnit = isBalance\n ? normalizedValue < 0\n ? \"left\"\n : normalizedValue > 0\n ? \"right\"\n : \"center\"\n : (unit ?? \"level\");\n const rotation = isCenteredMode ? rawPercent * 2.64 : rawPercent * 2.64 - 132;\n const powerOffset = `${184 - 184 * valueSweep}`;\n const balanceDash = `${balancePercent * 100} 100`;\n const tickCount = isTuner ? 12 : isBalance ? 10 : 8;\n const tickMarks = Array.from({ length: tickCount }, (_, index) => index);\n const allowsActiveToggle = clickTogglesActive ?? mode === \"power\";\n const isPressed = isDragging;\n const ringStroke = currentActive ? \"#ff00ff\" : isLight ? \"#8f8c87\" : \"#55595C\";\n const ringOpacity = currentActive ? 1 : 0.28;\n\n const normalizedValueRef = React.useRef(normalizedValue);\n React.useEffect(() => {\n normalizedValueRef.current = normalizedValue;\n }, [normalizedValue]);\n\n React.useEffect(() => {\n activeAtPointerDownRef.current = currentActive;\n }, [currentActive]);\n\n const setValue = React.useCallback(\n (nextValue: number) => {\n const clampedValue = clamp(roundToStep(nextValue, safeStep, safeMin), safeMin, safeMax);\n\n if (!isControlled) {\n setUncontrolledValue(clampedValue);\n }\n\n onValueChange?.(clampedValue);\n },\n [isControlled, onValueChange, safeMax, safeMin, safeStep],\n );\n\n const setActive = React.useCallback(\n (nextActive: boolean) => {\n if (!isActiveControlled) {\n setUncontrolledActive(nextActive);\n }\n onActiveChange?.(nextActive);\n },\n [isActiveControlled, onActiveChange],\n );\n\n React.useEffect(() => {\n if (!isDragging || disabled) {\n return;\n }\n\n const handlePointerMove = (event: PointerEvent) => {\n if (dragPointerIdRef.current !== event.pointerId) {\n return;\n }\n\n if (lastPointerXRef.current === null || lastPointerYRef.current === null) {\n lastPointerXRef.current = event.clientX;\n lastPointerYRef.current = event.clientY;\n return;\n }\n\n const deltaX = event.clientX - lastPointerXRef.current;\n const deltaY = event.clientY - lastPointerYRef.current;\n const delta = Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : -deltaY;\n lastPointerXRef.current = event.clientX;\n lastPointerYRef.current = event.clientY;\n\n if (delta === 0) {\n return;\n }\n\n didDragRef.current = true;\n event.preventDefault();\n const dragPixelsPerStep = (isTuner ? 6 : 8) / Math.max(dragMultiplier, 0.1);\n dragAccumulatorRef.current += delta / dragPixelsPerStep;\n\n const wholeSteps = Math.trunc(dragAccumulatorRef.current);\n if (wholeSteps === 0) {\n return;\n }\n\n dragAccumulatorRef.current -= wholeSteps;\n setValue(normalizedValueRef.current + wholeSteps * safeStep);\n };\n\n const handlePointerUp = (event: PointerEvent) => {\n if (dragPointerIdRef.current !== event.pointerId) {\n return;\n }\n\n if (knobRef.current?.hasPointerCapture(event.pointerId)) {\n knobRef.current.releasePointerCapture(event.pointerId);\n }\n\n dragPointerIdRef.current = null;\n lastPointerXRef.current = null;\n lastPointerYRef.current = null;\n dragAccumulatorRef.current = 0;\n const shouldToggleActive = allowsActiveToggle && !disabled && !didDragRef.current;\n didDragRef.current = false;\n setIsDragging(false);\n inputRef.current?.select();\n\n if (shouldToggleActive) {\n setActive(!activeAtPointerDownRef.current);\n }\n };\n\n const handlePointerCancel = (event: PointerEvent) => {\n if (dragPointerIdRef.current !== event.pointerId) {\n return;\n }\n\n if (knobRef.current?.hasPointerCapture(event.pointerId)) {\n knobRef.current.releasePointerCapture(event.pointerId);\n }\n\n dragPointerIdRef.current = null;\n lastPointerXRef.current = null;\n lastPointerYRef.current = null;\n dragAccumulatorRef.current = 0;\n didDragRef.current = false;\n setIsDragging(false);\n };\n\n window.addEventListener(\"pointermove\", handlePointerMove, { capture: true });\n window.addEventListener(\"pointerup\", handlePointerUp, { capture: true });\n window.addEventListener(\"pointercancel\", handlePointerCancel, { capture: true });\n\n return () => {\n window.removeEventListener(\"pointermove\", handlePointerMove, { capture: true });\n window.removeEventListener(\"pointerup\", handlePointerUp, { capture: true });\n window.removeEventListener(\"pointercancel\", handlePointerCancel, { capture: true });\n };\n }, [allowsActiveToggle, disabled, dragMultiplier, isDragging, safeStep, setActive, setValue]);\n\n React.useEffect(() => {\n const host = hostRef.current;\n if (!host || disabled) {\n return;\n }\n\n const handleWheel = (event: WheelEvent) => {\n event.preventDefault();\n event.stopPropagation();\n const dominantDelta =\n Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;\n if (dominantDelta === 0) {\n return;\n }\n if (dominantDelta < 0) {\n setValue(normalizedValueRef.current - wheelStep);\n } else {\n setValue(normalizedValueRef.current + wheelStep);\n }\n };\n\n host.addEventListener(\"wheel\", handleWheel, { passive: false });\n return () => host.removeEventListener(\"wheel\", handleWheel);\n }, [disabled, setValue, wheelStep]);\n\n const handleChange = (event: React.ChangeEvent) => {\n const nextValue =\n event.currentTarget.value === \"\" ? safeMin : Number(event.currentTarget.value);\n if (Number.isFinite(nextValue)) {\n setValue(nextValue);\n }\n };\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (disabled) {\n return;\n }\n\n switch (event.key) {\n case \"ArrowUp\":\n case \"ArrowRight\":\n case \"+\":\n event.preventDefault();\n setValue(normalizedValue + safeStep);\n break;\n case \"ArrowDown\":\n case \"ArrowLeft\":\n case \"-\":\n event.preventDefault();\n setValue(normalizedValue - safeStep);\n break;\n case \"Home\":\n event.preventDefault();\n setValue(isBalance ? 0 : safeMin);\n break;\n case \"End\":\n event.preventDefault();\n setValue(safeMax);\n break;\n default:\n break;\n }\n };\n\n const handlePointerDown = (event: React.PointerEvent) => {\n if (disabled) {\n return;\n }\n\n event.preventDefault();\n activeAtPointerDownRef.current = currentActive;\n dragPointerIdRef.current = event.pointerId;\n lastPointerXRef.current = event.clientX;\n lastPointerYRef.current = event.clientY;\n dragAccumulatorRef.current = 0;\n didDragRef.current = false;\n knobRef.current?.setPointerCapture(event.pointerId);\n setIsDragging(true);\n inputRef.current?.focus({ preventScroll: true });\n };\n\n const defaultReset = isCenteredMode ? 0 : defaultValue;\n\n const handleDoubleClick = () => {\n if (disabled) {\n return;\n }\n\n setValue(defaultReset);\n };\n\n return (\n \n setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onPointerDown={handlePointerDown}\n onDoubleClick={handleDoubleClick}\n role={allowsActiveToggle ? \"button\" : undefined}\n tabIndex={allowsActiveToggle ? 0 : undefined}\n aria-pressed={allowsActiveToggle ? currentActive : undefined}\n aria-label={\n allowsActiveToggle ? `${label} power ${currentActive ? \"on\" : \"off\"}` : undefined\n }\n onKeyDown={(event) => {\n if (!allowsActiveToggle || disabled) {\n return;\n }\n\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n setActive(!currentActive);\n }\n }}\n >\n \n \n \n \n\n \n \n {tickMarks.map((mark) => {\n const angle = -132 + (264 / (tickCount - 1)) * mark;\n return (\n \n );\n })}\n \n \n {isBalance ? (\n hasBalanceHighlight ? (\n = 0 ? \"M50,10 A 40 40 0 0 1 80 76\" : \"M50,10 A 40 40 0 0 0 20 76\"\n }\n pathLength={100}\n fill=\"none\"\n stroke={ringStroke}\n strokeWidth=\"5\"\n strokeLinecap=\"round\"\n strokeDasharray={balanceDash}\n strokeOpacity={ringOpacity}\n />\n ) : null\n ) : (\n \n )}\n \n\n \n \n \n \n \n \n \n\n \n \n {formattedValue}\n \n \n {formattedUnit}\n \n \n\n \n\n \n {label}\n \n \n );\n}\n" }, { "type": "registry:component", "path": "kit-knobs-section.tsx", "target": "components/realmorphism/kit/kit-knobs-section.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { Knob } from \"@/components/realmorphism/ui/knob\";\n\nexport function KitKnobsSection() {\n const [power, setPower] = React.useState(42);\n const [tone, setTone] = React.useState(24);\n const [balance, setBalance] = React.useState(0);\n const [trim, setTrim] = React.useState(7);\n\n return (\n
\n
\n
\n

Rotary Surface

\n

\n Wheel for precise increments, drag for a quick sweep, press the dial face to toggle power,\n double-click to snap back to default.\n

\n
\n wheel-first tuning\n
\n
\n \n \n \n \n
\n
\n );\n}\n" }, { "type": "registry:component", "path": "kit-compact-rolling-picker-section.tsx", "target": "components/realmorphism/kit/kit-compact-rolling-picker-section.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { DocTypeRollingPicker } from \"../doc-type-rolling-picker\";\nimport { DOC_TYPE_ENTRIES, type DocTypeValue } from \"@/lib/realmorphism/doc-type-icon\";\n\nexport function KitCompactRollingPickerSection() {\n const [docType, setDocType] = React.useState(\"markdown\");\n const activeLabel = DOC_TYPE_ENTRIES.find((entry) => entry.value === docType)?.label ?? docType;\n\n return (\n
\n
\n
\n

Rolling Picker

\n

\n Compact toolbar variant of the same control: one visible row in the bar, neighbors on while\n you spin, icon when settled. Document types below are demo data only.\n

\n
\n control · compact\n
\n\n
\n
\n \n Operator toolbar\n \n \n \n
\n\n
\n
\n Selected\n
\n
{activeLabel}
\n
{docType}
\n
\n
\n
\n );\n}\n" }, { "type": "registry:component", "path": "kit-text-rolling-picker-section.tsx", "target": "components/realmorphism/kit/kit-text-rolling-picker-section.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { TextRollingPicker } from \"../text-rolling-picker\";\nimport {\n DEMO_TEXT_CATALOG,\n type DemoTextCatalogEntry,\n} from \"@/lib/realmorphism/demo-text-catalog\";\n\nexport type KitTextRollingPickerSectionProps = {\n catalog?: DemoTextCatalogEntry[];\n};\n\nexport function KitTextRollingPickerSection({\n catalog = DEMO_TEXT_CATALOG,\n}: KitTextRollingPickerSectionProps) {\n const [artId, setArtId] = React.useState(catalog[0]?.id ?? \"\");\n const active =\n (artId ? catalog.find((entry) => entry.id === artId) : undefined) ?? catalog[0] ?? null;\n const currentIndex = active ? catalog.findIndex((entry) => entry.id === active.id) : -1;\n\n React.useEffect(() => {\n if (!catalog.length) return;\n if (catalog.some((entry) => entry.id === artId)) return;\n setArtId(catalog[0]?.id ?? \"\");\n }, [artId, catalog]);\n\n return (\n
\n
\n
\n

Text Roller

\n

\n Compact variant with title text in the toolbar roller. Spin to browse; the ASCII line lands\n in the detail panel when you stop—not in the wheel itself.\n

\n
\n control · text\n
\n\n
\n
\n
\n \n 1-line toolbar\n \n \n
\n \n
\n
\n\n
\n \n Detail panel (example: 1-line ascii)\n \n
\n {active ? (\n
\n                  {active.content}\n                
\n ) : (\n
No catalog entries
\n )}\n
\n
\n
\n\n
\n
\n Selected\n
\n {active ? (\n <>\n
{active.title}
\n
{active.id}
\n {catalog.length > 1 && currentIndex >= 0 ? (\n
\n {currentIndex + 1} / {catalog.length}\n
\n ) : null}\n \n ) : (\n
\n )}\n
\n
\n
\n );\n}\n" }, { "type": "registry:component", "path": "kit-showroom-figlet-section.tsx", "target": "components/realmorphism/kit/kit-showroom-figlet-section.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n DEFAULT_DEMO_FIGLET_FONT,\n DEMO_FIGLET_FONTS,\n resolveDemoFigletValue,\n} from \"@/lib/realmorphism/demo-figlet-catalog\";\nimport {\n ShowroomFontPicker,\n type ShowroomFontWheelPreviewRender,\n} from \"../showroom-font-picker\";\nimport { ShowroomFontPreviewPanel } from \"../showroom-font-preview-panel\";\n\nexport type KitShowroomFigletSectionProps = {\n fonts?: readonly string[];\n defaultFont?: string;\n previewText?: string;\n renderWheelPreview?: ShowroomFontWheelPreviewRender;\n renderDetailPreview?: (font: string, previewText: string) => React.ReactNode;\n};\n\nexport function KitShowroomFigletSection({\n fonts = DEMO_FIGLET_FONTS,\n defaultFont = DEFAULT_DEMO_FIGLET_FONT,\n previewText = \"ECHO\",\n renderWheelPreview,\n renderDetailPreview,\n}: KitShowroomFigletSectionProps) {\n const [font, setFont] = React.useState(defaultFont);\n const resolved = resolveDemoFigletValue(font, fonts);\n const currentIndex = fonts.findIndex((name) => name.toLowerCase() === resolved.toLowerCase());\n\n React.useEffect(() => {\n if (resolved === font) return;\n setFont(resolved);\n }, [resolved, font]);\n\n return (\n
\n
\n
\n

Rolling Picker

\n

\n Showroom variant for long lists: a square wheel with a mirrored band, momentum scroll, and\n a selection row that can show rich preview plus label. Off-band rows stay text-only so the\n layout does not jump when you stop.\n

\n
\n control · showroom\n
\n\n
\n
\n \n \n
\n\n
\n
\n Selected\n
\n
{resolved}
\n {fonts.length > 1 && currentIndex >= 0 ? (\n
\n {currentIndex + 1} / {fonts.length}\n
\n ) : null}\n
\n
\n\n
\n \n Detail panel (example: figlet)\n \n {renderDetailPreview ? (\n renderDetailPreview(resolved, previewText)\n ) : (\n \n )}\n
\n \n
\n );\n}\n" }, { "type": "registry:component", "path": "kit-showroom.tsx", "target": "components/realmorphism/kit/kit-showroom.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { Checkbox } from \"@/components/realmorphism/ui/checkbox\";\nimport { Input } from \"@/components/realmorphism/ui/input\";\nimport { Label } from \"@/components/realmorphism/ui/label\";\nimport { Switch } from \"@/components/realmorphism/ui/switch\";\nimport { Toggle } from \"@/components/realmorphism/ui/toggle\";\nimport { KitCompactRollingPickerSection } from \"./kit-compact-rolling-picker-section\";\nimport { KitKnobsSection } from \"./kit-knobs-section\";\nimport {\n KitShowroomFigletSection,\n type KitShowroomFigletSectionProps,\n} from \"./kit-showroom-figlet-section\";\nimport {\n KitTextRollingPickerSection,\n type KitTextRollingPickerSectionProps,\n} from \"./kit-text-rolling-picker-section\";\nimport type { DemoTextCatalogEntry } from \"@/lib/realmorphism/demo-text-catalog\";\n\nexport type KitInstallCommand = {\n label: string;\n command: string;\n};\n\nconst REGISTRY_ITEMS = [\n {\n name: \"realmorphism\",\n type: \"registry:theme\",\n summary: \"Tokens, color planes, radius, hard wall shadow, and motion CSS.\",\n },\n {\n name: \"realmorphism-base\",\n type: \"registry:block\",\n summary: \"Turnkey shadcn wrappers for tactile controls and layout primitives.\",\n },\n {\n name: \"realmorphism-rolling-pickers\",\n type: \"registry:block\",\n summary: \"Compact, text, and showroom Y-axis rolling pickers (optional add-on).\",\n },\n {\n name: \"realmorphism-kit\",\n type: \"registry:block\",\n summary: \"Full KitShowroom with knobs and all three picker sections.\",\n },\n {\n name: \"realmorphism-site\",\n type: \"registry:block\",\n summary: \"Portable /registry page backed by KitShowroom.\",\n },\n {\n name: \"realmorphism (package)\",\n type: \"npm package\",\n summary: \"pnpm add realmorphism — same pickers + kit via import (Echo monorepo uses file:../realmorphism).\",\n },\n] as const;\n\nconst SWATCHES = [\n [\"Host\", \"#060708\"],\n [\"Raised\", \"#0e1011\"],\n [\"Face\", \"#171c19\"],\n [\"Wall\", \"#2a6b56\"],\n [\"Hover\", \"#3a9174\"],\n [\"Amber\", \"#8a6530\"],\n [\"Signal\", \"#7dffb4\"],\n [\"Ink\", \"#e8efeb\"],\n] as const;\n\nfunction defaultInstallCommands(origin: string): KitInstallCommand[] {\n return [\n { label: \"Theme\", command: `npx shadcn@latest add ${origin}/registry/realmorphism.json` },\n {\n label: \"Base controls\",\n command: `npx shadcn@latest add ${origin}/registry/realmorphism-base.json`,\n },\n {\n label: \"Rolling pickers\",\n command: `npx shadcn@latest add ${origin}/registry/realmorphism-rolling-pickers.json`,\n },\n {\n label: \"Kit showroom\",\n command: `npx shadcn@latest add ${origin}/registry/realmorphism-kit.json`,\n },\n {\n label: \"Package (monorepo / npm)\",\n command: \"pnpm add realmorphism\",\n },\n ];\n}\n\nexport type KitShowroomProps = {\n variant?: \"page\" | \"embedded\";\n badgeLabel?: string;\n installCommands?: KitInstallCommand[];\n showFormControls?: boolean;\n textCatalog?: DemoTextCatalogEntry[];\n figlet?: KitShowroomFigletSectionProps;\n};\n\nexport function KitShowroom({\n variant = \"page\",\n badgeLabel = \"Realmorphism Registry\",\n installCommands,\n showFormControls = false,\n textCatalog,\n figlet,\n}: KitShowroomProps) {\n const isEmbedded = variant === \"embedded\";\n const [toggleSelected, setToggleSelected] = React.useState(true);\n const [opsAlert, setOpsAlert] = React.useState(true);\n const [opsAck, setOpsAck] = React.useState(false);\n const [opsLatch, setOpsLatch] = React.useState(true);\n\n const [installOrigin, setInstallOrigin] = React.useState(\"https://your-origin.example\");\n React.useEffect(() => {\n setInstallOrigin(`${window.location.origin}`.replace(/\\/$/, \"\"));\n }, []);\n\n const commands = installCommands ?? defaultInstallCommands(installOrigin);\n\n return (\n \n
\n
\n
\n
\n {badgeLabel}\n
\n
\n

\n Realmorphism\n

\n

\n A blocky tactile shadcn theme for operational interfaces. The colors define the\n planes; the motion tells the operator an action is available.\n

\n
\n
\n
\n
\n Install\n
\n {commands.map((item) => (\n
\n
{item.label}
\n
\n                  {item.command}\n                
\n
\n ))}\n
\n
\n\n
\n {SWATCHES.map(([label, color]) => (\n
\n
\n
{label}
\n
{color}
\n
\n ))}\n
\n\n
\n
\n
\n
\n

Action Surface

\n

Hover and press the controls.

\n
\n motion = affordance\n
\n
\n \n setToggleSelected((pressed) => !pressed)}\n >\n \n \n Unselected\n \n \n {toggleSelected ? \"Selected\" : \"Unselected\"}\n \n \n \n {showFormControls ? (\n \n Toggle {opsLatch ? \"ON\" : \"OFF\"}\n \n ) : null}\n \n Caution\n \n \n Danger\n \n \n Disabled\n \n
\n\n {showFormControls ? (\n
\n
\n Form controls\n
\n
\n \n \n
\n
\n \n \n
\n
\n setOpsAck(value === true)}\n />\n \n
\n
\n ) : null}\n
\n\n
\n {REGISTRY_ITEMS.map((item) => (\n
\n
\n
{item.name}
\n
\n {item.type}\n
\n
\n

{item.summary}

\n
\n ))}\n
\n
\n\n \n \n \n \n\n
\n

Community Upload Notes

\n

\n Tweakcn can carry the Realmorphism colors and tokens. The shadcn registry carries the\n installable part: theme JSON, wrappers, and this showroom page. Publish the colors on\n tweakcn, then point builders to this registry when they need the full motion layer.\n

\n
\n
\n \n );\n}\n\nexport type { KitTextRollingPickerSectionProps, KitShowroomFigletSectionProps };\n" }, { "type": "registry:lib", "path": "index.ts", "target": "components/realmorphism/index.ts", "content": "export { DocTypeRollingPicker } from \"./doc-type-rolling-picker\";\nexport { TextRollingPicker } from \"./text-rolling-picker\";\nexport {\n ShowroomFontPicker,\n type ShowroomFontWheelPreviewRender,\n} from \"./showroom-font-picker\";\nexport { ShowroomFontPreviewSlide } from \"./showroom-font-preview-slide\";\nexport { ShowroomFontPreviewPanel } from \"./showroom-font-preview-panel\";\nexport {\n RollingPicker,\n type RollingPickerItem,\n type RollingPickerProps,\n} from \"./ui/rolling-picker\";\nexport { KitShowroom, type KitShowroomProps, type KitInstallCommand } from \"./kit/kit-showroom\";\nexport { KitCompactRollingPickerSection } from \"./kit/kit-compact-rolling-picker-section\";\nexport {\n KitTextRollingPickerSection,\n type KitTextRollingPickerSectionProps,\n} from \"./kit/kit-text-rolling-picker-section\";\nexport {\n KitShowroomFigletSection,\n type KitShowroomFigletSectionProps,\n} from \"./kit/kit-showroom-figlet-section\";\nexport { KitKnobsSection } from \"./kit/kit-knobs-section\";\nexport {\n catalogToRollingItems,\n resolveCatalogPickerValue,\n rollingPickerLayoutForMode,\n type CatalogToRollingItemsOptions,\n type RollingPickerRowMode,\n} from \"@/lib/realmorphism/catalog-to-rolling-items\";\nexport {\n DEMO_TEXT_CATALOG,\n resolveDemoTextValue,\n type DemoTextCatalogEntry,\n} from \"@/lib/realmorphism/demo-text-catalog\";\nexport {\n DEMO_FIGLET_FONTS,\n DEFAULT_DEMO_FIGLET_FONT,\n resolveDemoFigletValue,\n} from \"@/lib/realmorphism/demo-figlet-catalog\";\n" } ], "meta": { "installNote": "Requires realmorphism theme, base wrappers, and rolling pickers registry items.", "components": [ "KitShowroom" ] } }