{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "glass-ai-card", "type": "registry:ui", "title": "Glass AI Card", "description": "iOS 26 liquid-glass content card with animated skeleton lines and hue-rotating gradient AI action button.", "dependencies": [ "motion" ], "files": [ { "path": "registry/ruixenui/glass-ai-card.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion } from \"motion/react\";\n\n/* ── theme ────────────────────────────────────────────────── */\n\nconst CSS = `\n.gc{\n --gc-glass:linear-gradient(135deg,rgba(255,255,255,.78),rgba(255,255,255,.62));\n --gc-border:rgba(0,0,0,.06);\n --gc-shadow:0 0 1px rgba(0,0,0,.04),0 2px 8px rgba(0,0,0,.04),0 12px 32px rgba(0,0,0,.06),inset 0 1px 0 rgba(255,255,255,.8);\n --gc-bar:rgba(0,0,0,.06);\n --gc-bar-shimmer:rgba(0,0,0,.1);\n --gc-dim:rgba(0,0,0,.45);\n --gc-mid:rgba(0,0,0,.65);\n --gc-hi:rgba(0,0,0,.88);\n --gc-btn-bg:rgba(255,255,255,.92);\n --gc-btn-border:rgba(0,0,0,.06);\n --gc-btn-shadow:0 1px 3px rgba(0,0,0,.06);\n --gc-hover:rgba(0,0,0,.03);\n --gc-grad-a:#f472b6;\n --gc-grad-b:#a78bfa;\n}\n.dark .gc,[data-theme=\"dark\"] .gc{\n --gc-glass:linear-gradient(135deg,rgba(255,255,255,.05),rgba(255,255,255,.02));\n --gc-border:rgba(255,255,255,.07);\n --gc-shadow:0 1px 3px rgba(0,0,0,.12),inset 0 1px 0 rgba(255,255,255,.04);\n --gc-bar:rgba(255,255,255,.06);\n --gc-bar-shimmer:rgba(255,255,255,.1);\n --gc-dim:rgba(255,255,255,.35);\n --gc-mid:rgba(255,255,255,.55);\n --gc-hi:rgba(255,255,255,.88);\n --gc-btn-bg:rgba(255,255,255,.06);\n --gc-btn-border:rgba(255,255,255,.08);\n --gc-btn-shadow:0 1px 3px rgba(0,0,0,.12);\n --gc-hover:rgba(255,255,255,.04);\n --gc-grad-a:#f9a8d4;\n --gc-grad-b:#c4b5fd;\n}\n@keyframes gc-shimmer{\n 0%{background-position:-200% 0}\n 100%{background-position:200% 0}\n}\n@keyframes gc-hue{\n 0%{filter:hue-rotate(0deg)}\n 100%{filter:hue-rotate(360deg)}\n}\n`;\n\n/* ── sound ────────────────────────────────────────────────── */\n\nlet _a: AudioContext, _b: AudioBuffer;\nconst tick = () => {\n if (typeof window === \"undefined\") return;\n if (!_a) {\n _a = new AudioContext();\n _b = _a.createBuffer(1, (_a.sampleRate * 0.003) | 0, _a.sampleRate);\n const d = _b.getChannelData(0);\n for (let i = 0; i < d.length; i++)\n d[i] = (Math.random() * 2 - 1) * (1 - i / d.length) ** 4;\n }\n const s = _a.createBufferSource();\n s.buffer = _b;\n const g = _a.createGain();\n g.gain.value = 0.08;\n s.connect(g).connect(_a.destination);\n s.start();\n};\n\n/* ── skeleton lines layout ───────────────────────────────── */\n\nconst DEFAULT_LINES: number[][] = [[100], [40, 20, 40], [40, 20], [20, 80]];\n\n/* ── component ───────────────────────────────────────────── */\n\ninterface GlassAICardProps {\n lines?: number[][];\n actionLabel?: string;\n onAction?: () => void;\n sound?: boolean;\n style?: React.CSSProperties;\n}\n\nexport default function GlassAICard({\n lines = DEFAULT_LINES,\n actionLabel = \"Generate\",\n onAction,\n sound = true,\n style,\n}: GlassAICardProps) {\n const [pressed, setPressed] = React.useState(false);\n\n const handleClick = () => {\n if (sound) tick();\n setPressed(true);\n onAction?.();\n setTimeout(() => setPressed(false), 600);\n };\n\n return (\n <>\n