bookwiz.io / components / DiffViewer.tsx
DiffViewer.tsx
Raw
'use client'

import { useEffect, useRef } from 'react'
import { DiffEditor } from '@monaco-editor/react'
import { IoCheckmarkOutline, IoArrowBackOutline } from 'react-icons/io5'

interface DiffViewerProps {
  originalContent: string
  modifiedContent: string
  fileName?: string
  className?: string
  onRevert?: () => void
  onAccept?: () => void
  isLoading?: boolean
}

export default function DiffViewer({
  originalContent,
  modifiedContent,
  fileName,
  className = '',
  onRevert,
  onAccept,
  isLoading = false
}: DiffViewerProps) {
  const diffEditorRef = useRef<any>(null)

  const handleEditorDidMount = (editor: any) => {
    diffEditorRef.current = editor
    
    // Configure the diff editor
    editor.updateOptions({
      readOnly: true,
      renderSideBySide: true,
      ignoreTrimWhitespace: false,
      renderWhitespace: 'boundary',
      wordWrap: 'on',
      fontSize: 13,
      lineHeight: 18,
      minimap: { enabled: false },
      folding: false,
      lineNumbers: 'on',
      glyphMargin: false,
      scrollBeyondLastLine: false,
    })
  }

  return (
    <div className={`h-full flex flex-col bg-slate-900/40 backdrop-blur-sm ${className}`}>
      {/* Monaco Diff Editor */}
      <div className="flex-1">
        <DiffEditor
          original={originalContent}
          modified={modifiedContent}
          language="markdown" // Default to markdown, could be made configurable
          theme="vs-dark"
          onMount={handleEditorDidMount}
          options={{
            readOnly: true,
            renderSideBySide: true,
            ignoreTrimWhitespace: false,
            renderWhitespace: 'boundary',
            wordWrap: 'on',
            fontSize: 13,
            lineHeight: 18,
            minimap: { enabled: false },
            folding: false,
            lineNumbers: 'on',
            glyphMargin: false,
            scrollBeyondLastLine: false,
            automaticLayout: true,
          }}
        />
      </div>
      
      {/* Minimal status bar */}
      <div className="h-8 bg-slate-800/95 backdrop-blur-md border-t border-slate-700/50 flex items-center justify-between px-4 text-xs text-slate-400">
        <div className="flex items-center gap-4">
          <span className="text-slate-300">Diff View</span>
          {fileName && (
            <span className="text-slate-500 bg-slate-700/30 px-1.5 py-0.5 rounded text-[10px]">
              {fileName.split('.').pop()?.toUpperCase() || 'TXT'}
            </span>
          )}
        </div>
        {(onRevert || onAccept) && (
          <div className="flex items-center gap-1.5">
            {onRevert && (
              <button
                onClick={onRevert}
                disabled={isLoading}
                className="group relative overflow-hidden bg-red-500/20 hover:bg-red-500/30 border border-red-500/30 text-red-400 hover:text-red-300 px-2 py-0.5 rounded text-[10px] font-medium transition-all duration-150 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1"
              >
                {isLoading ? (
                  <>
                    <div className="w-2.5 h-2.5 border border-current border-t-transparent rounded-full animate-spin opacity-70"></div>
                    <span>Reverting...</span>
                  </>
                ) : (
                  <>
                    <IoArrowBackOutline className="w-2.5 h-2.5" />
                    <span>Revert</span>
                  </>
                )}
              </button>
            )}
            {onAccept && (
              <button
                onClick={onAccept}
                disabled={isLoading}
                className="group relative overflow-hidden bg-slate-700 hover:bg-slate-600 border border-slate-600 text-slate-200 px-2 py-0.5 rounded text-[10px] font-medium transition-all duration-150 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1"
              >
                {isLoading ? (
                  <>
                    <div className="w-2.5 h-2.5 border border-current border-t-transparent rounded-full animate-spin opacity-70"></div>
                    <span>Accepting...</span>
                  </>
                ) : (
                  <>
                    <IoCheckmarkOutline className="w-2.5 h-2.5" />
                    <span>Accept</span>
                  </>
                )}
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  )
}