{"name":"diff-editor","title":"Diff Editor","type":"registry:ui","files":[{"path":"src/components/ui/diff-editor.tsx","type":"registry:ui","target":"@ui/diff-editor.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"./lib/utils\";\n\nexport interface DiffEditorProps extends React.HTMLAttributes<HTMLDivElement> {\n  before: string;\n  after: string;\n  readOnly?: boolean;\n  onBeforeChange?: (v: string) => void;\n  onAfterChange?: (v: string) => void;\n}\n\n/** Side-by-side editable diff comparison editor. */\nexport function DiffEditor({ before, after, readOnly, onBeforeChange, onAfterChange, className, ...props }: DiffEditorProps) {\n  const beforeLines = before.split(\"\\n\");\n  const afterLines = after.split(\"\\n\");\n  const maxLines = Math.max(beforeLines.length, afterLines.length);\n  const beforeSet = new Set(beforeLines);\n  const afterSet = new Set(afterLines);\n\n  const rowTone = (idx: number): \"same\" | \"added\" | \"removed\" | \"changed\" => {\n    const b = beforeLines[idx];\n    const a = afterLines[idx];\n    if (b === a) return \"same\";\n    if (b === undefined) return \"added\";\n    if (a === undefined) return \"removed\";\n    if (!afterSet.has(b) || !beforeSet.has(a)) return \"changed\";\n    return \"same\";\n  };\n\n  return (\n    <div className={cn(\"w-full overflow-hidden rounded-lg border border-border bg-card\", className)} {...props}>\n      <style>{`@keyframes deIn { from { opacity: 0; } to { opacity: 1; } }`}</style>\n      <div className=\"grid grid-cols-2 divide-x divide-border border-b border-border bg-secondary/60 font-mono text-[10px] uppercase tracking-wider text-muted-foreground\">\n        <span className=\"px-3 py-1.5\">Original</span>\n        <span className=\"px-3 py-1.5\">After</span>\n      </div>\n      <div className=\"grid max-h-64 grid-cols-2 divide-x divide-border overflow-auto\">\n        <textarea\n          value={before}\n          onChange={(e) => onBeforeChange?.(e.target.value)}\n          readOnly={readOnly}\n          aria-label=\"Previous version\"\n          spellCheck={false}\n          className=\"min-h-40 resize-none bg-transparent p-3 font-mono text-xs leading-5 text-foreground focus-visible:outline-none\"\n        />\n        <textarea\n          value={after}\n          onChange={(e) => onAfterChange?.(e.target.value)}\n          readOnly={readOnly}\n          aria-label=\"Next version\"\n          spellCheck={false}\n          className={cn(\n            \"min-h-40 resize-none bg-transparent p-3 font-mono text-xs leading-5 text-foreground focus-visible:outline-none animate-[deIn_0.3s_ease-out] motion-reduce:animate-none\",\n            maxLines > 0 && afterLines.some((l, i) => rowTone(i) !== \"same\") && \"bg-success/5\"\n          )}\n        />\n      </div>\n    </div>\n  );\n}\n"}],"registryDependencies":["https://ui.bigbullapp.com/r/utils.json"]}