{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "version-history-demo",
  "dependencies": [
    "@platejs/basic-nodes",
    "@platejs/diff",
    "lodash"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "src/registry/examples/version-history-demo.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\n\nimport {\n  type DiffOperation,\n  type DiffUpdate,\n  computeDiff,\n  withGetFragmentExcludeDiff,\n} from '@platejs/diff';\nimport { cloneDeep } from 'lodash';\nimport { type Value, createSlatePlugin, KEYS } from 'platejs';\nimport { createPlatePlugin, toPlatePlugin, useSelected } from 'platejs/react';\nimport {\n  type PlateElementProps,\n  type PlateLeafProps,\n  type PlateProps,\n  createPlateEditor,\n  Plate,\n  PlateContent,\n  PlateElement,\n  PlateLeaf,\n  usePlateEditor,\n} from 'platejs/react';\n\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\nimport { BasicMarksKit } from '@/registry/components/editor/plugins/basic-marks-kit';\n\nconst InlinePlugin = createPlatePlugin({\n  key: 'inline',\n  node: { isElement: true, isInline: true },\n});\n\nconst InlineVoidPlugin = createPlatePlugin({\n  key: 'inline-void',\n  node: { isElement: true, isInline: true, isVoid: true },\n});\n\nconst diffOperationColors: Record<DiffOperation['type'], string> = {\n  delete: 'bg-red-200',\n  insert: 'bg-green-200',\n  update: 'bg-blue-200',\n};\n\nconst describeUpdate = ({ newProperties, properties }: DiffUpdate) => {\n  const addedProps: string[] = [];\n  const removedProps: string[] = [];\n  const updatedProps: string[] = [];\n\n  Object.keys(newProperties).forEach((key) => {\n    const oldValue = properties[key];\n    const newValue = newProperties[key];\n\n    if (oldValue === undefined) {\n      addedProps.push(key);\n\n      return;\n    }\n    if (newValue === undefined) {\n      removedProps.push(key);\n\n      return;\n    }\n\n    updatedProps.push(key);\n  });\n\n  const descriptionParts: string[] = [];\n\n  if (addedProps.length > 0) {\n    descriptionParts.push(`Added ${addedProps.join(', ')}`);\n  }\n  if (removedProps.length > 0) {\n    descriptionParts.push(`Removed ${removedProps.join(', ')}`);\n  }\n  if (updatedProps.length > 0) {\n    updatedProps.forEach((key) => {\n      descriptionParts.push(\n        `Updated ${key} from ${properties[key]} to ${newProperties[key]}`\n      );\n    });\n  }\n\n  return descriptionParts.join('\\n');\n};\n\nconst InlineElement = ({ children, ...props }: PlateElementProps) => (\n  <PlateElement {...props} as=\"span\" className=\"rounded-sm bg-slate-200/50 p-1\">\n    {children}\n  </PlateElement>\n);\n\nconst InlineVoidElement = ({ children, ...props }: PlateElementProps) => {\n  const selected = useSelected();\n\n  return (\n    <PlateElement {...props} as=\"span\">\n      <span\n        className={cn(\n          'rounded-sm bg-slate-200/50 p-1',\n          selected && 'bg-blue-500 text-white'\n        )}\n        contentEditable={false}\n      >\n        Inline void\n      </span>\n      {children}\n    </PlateElement>\n  );\n};\n\nconst DiffPlugin = toPlatePlugin(\n  createSlatePlugin({\n    key: 'diff',\n    node: { isLeaf: true },\n  }).overrideEditor(withGetFragmentExcludeDiff),\n  {\n    render: {\n      node: DiffLeaf,\n      aboveNodes:\n        () =>\n        ({ children, editor, element }) => {\n          if (!element.diff) return children;\n\n          const diffOperation = element.diffOperation as DiffOperation;\n\n          const label = (\n            {\n              delete: 'deletion',\n              insert: 'insertion',\n              update: 'update',\n            } as any\n          )[diffOperation.type];\n\n          const Component = editor.api.isInline(element) ? 'span' : 'div';\n\n          return (\n            <Component\n              className={diffOperationColors[diffOperation.type]}\n              title={\n                diffOperation.type === 'update'\n                  ? describeUpdate(diffOperation)\n                  : undefined\n              }\n              aria-label={label}\n            >\n              {children}\n            </Component>\n          );\n        },\n    },\n  }\n);\n\nfunction DiffLeaf({ children, ...props }: PlateLeafProps) {\n  const diffOperation = props.leaf.diffOperation as DiffOperation;\n\n  const _Component = (\n    {\n      delete: 'del',\n      insert: 'ins',\n      update: 'span',\n    } as any\n  )[diffOperation.type];\n\n  return (\n    <PlateLeaf\n      {...props}\n      // as={Component}\n      className={diffOperationColors[diffOperation.type]}\n      attributes={{\n        ...props.attributes,\n        title:\n          diffOperation.type === 'update'\n            ? describeUpdate(diffOperation)\n            : undefined,\n      }}\n    >\n      {children}\n    </PlateLeaf>\n  );\n}\n\nconst initialValue: Value = [\n  {\n    children: [{ text: 'This is a version history demo.' }],\n    type: KEYS.p,\n  },\n  {\n    children: [\n      { text: 'Try editing the ' },\n      { bold: true, text: 'text and see what' },\n      { text: ' happens.' },\n    ],\n    type: KEYS.p,\n  },\n  {\n    children: [\n      { text: 'This is an ' },\n      { children: [{ text: '' }], type: InlineVoidPlugin.key },\n      { text: '. Try removing it.' },\n    ],\n    type: KEYS.p,\n  },\n  {\n    children: [\n      { text: 'This is an ' },\n      { children: [{ text: 'editable inline' }], type: InlinePlugin.key },\n      { text: '. Try editing it.' },\n    ],\n    type: KEYS.p,\n  },\n];\n\nexport const createVersionSnapshot = (value: Value): Value => cloneDeep(value);\n\nconst basePlugins = [\n  ...BasicMarksKit,\n  InlinePlugin.withComponent(InlineElement),\n  InlineVoidPlugin.withComponent(InlineVoidElement),\n];\n\nconst diffPlugins = [...basePlugins, DiffPlugin];\n\nfunction VersionHistoryPlate(props: Omit<PlateProps, 'children'>) {\n  return (\n    <Plate {...props}>\n      <PlateContent className=\"rounded-md border p-3\" />\n    </Plate>\n  );\n}\n\ntype DiffProps = {\n  current: Value;\n  previous: Value;\n};\n\nfunction Diff({ current, previous }: DiffProps) {\n  const diffValue = React.useMemo(() => {\n    const editor = createPlateEditor({\n      plugins: diffPlugins,\n    });\n\n    return computeDiff(\n      createVersionSnapshot(previous),\n      createVersionSnapshot(current),\n      {\n        isInline: editor.api.isInline,\n        lineBreakChar: '¶',\n      }\n    ) as Value;\n  }, [previous, current]);\n\n  const editor = usePlateEditor(\n    {\n      plugins: diffPlugins,\n      value: diffValue,\n    },\n    [diffValue]\n  );\n\n  return (\n    <>\n      <VersionHistoryPlate\n        key={JSON.stringify(diffValue)}\n        readOnly\n        editor={editor}\n      />\n\n      {/* <pre>{JSON.stringify(diffValue, null, 2)}</pre> */}\n    </>\n  );\n}\n\nexport default function VersionHistoryDemo() {\n  const [revisions, setRevisions] = React.useState<Value[]>(() => [\n    createVersionSnapshot(initialValue),\n  ]);\n  const [selectedRevisionIndex, setSelectedRevisionIndex] =\n    React.useState<number>(0);\n  const [value, setValue] = React.useState<Value>(() =>\n    createVersionSnapshot(initialValue)\n  );\n\n  const selectedRevisionValue = React.useMemo(\n    () => revisions[selectedRevisionIndex],\n    [revisions, selectedRevisionIndex]\n  );\n\n  const saveRevision = () => {\n    setRevisions([...revisions, createVersionSnapshot(value)]);\n  };\n\n  const editor = usePlateEditor({\n    plugins: basePlugins,\n    value: createVersionSnapshot(initialValue),\n  });\n\n  const editorRevision = usePlateEditor(\n    {\n      plugins: basePlugins,\n      value: selectedRevisionValue,\n    },\n    [selectedRevisionValue]\n  );\n\n  return (\n    <div className=\"flex flex-col gap-3 p-3\">\n      <Button onClick={saveRevision}>Save revision</Button>\n\n      <VersionHistoryPlate\n        onChange={({ value }) => setValue(createVersionSnapshot(value))}\n        editor={editor}\n      />\n\n      <label>\n        Revision to compare:\n        <select\n          className=\"rounded-md border p-1\"\n          onChange={(e) => setSelectedRevisionIndex(Number(e.target.value))}\n        >\n          {revisions.map((_, i) => (\n            <option key={i} value={i}>\n              Revision {i + 1}\n            </option>\n          ))}\n        </select>\n      </label>\n\n      <div className=\"grid gap-3 md:grid-cols-2\">\n        <div>\n          <h2>Revision {selectedRevisionIndex + 1}</h2>\n          <VersionHistoryPlate\n            key={selectedRevisionIndex}\n            readOnly\n            editor={editorRevision}\n          />\n        </div>\n\n        <div>\n          <h2>Diff</h2>\n          <Diff current={value} previous={selectedRevisionValue} />\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:example"
    }
  ],
  "type": "registry:example"
}