oversci/app/components/Editor/MonacoEditor.tsx
2025-01-31 19:32:50 +08:00

143 lines
3.3 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unused-vars */
// app/components/Editor/MonacoEditor.tsx
'use client'
import { useRef, useCallback, useEffect } from 'react'
import Editor from '@monaco-editor/react'
import * as Y from 'yjs'
import { WebsocketProvider } from 'y-websocket'
import { MonacoBinding } from 'y-monaco'
import { message } from 'antd'
import { DocumentService } from '@/app/lib/services/document'
interface MonacoEditorProps {
docId: string
defaultValue?: string
onContentChange?: (content: string) => void
}
export default function MonacoEditor({
docId,
defaultValue = '',
onContentChange
}: MonacoEditorProps) {
const editorRef = useRef<any>(null)
const monacoRef = useRef<any>(null)
// 自动保存防抖
const autoSaveDebounced = useCallback(
debounce(async (content: string) => {
try {
await DocumentService.saveDocument({
id: docId,
content,
})
message.success('自动保存成功')
} catch (error) {
message.error('自动保存失败'+error)
}
}, 2000),
[docId]
)
// 手动保存
const saveContent = async () => {
if (!editorRef.current) return
const content = editorRef.current.getValue()
try {
await DocumentService.saveDocument({
id: docId,
content,
})
message.success('保存成功')
} catch (error) {
message.error('保存失败'+error)
}
}
// 监听内容变化
const handleEditorChange = (value: string | undefined) => {
if (value === undefined) return
onContentChange?.(value)
autoSaveDebounced(value)
}
useEffect(() => {
if (!editorRef.current) return
const ydoc = new Y.Doc()
const provider = new WebsocketProvider(
'ws://localhost:1234',
docId,
ydoc
)
const type = ydoc.getText('monaco')
const binding = new MonacoBinding(
type,
editorRef.current.getModel(),
new Set([editorRef.current]),
provider.awareness
)
// 添加快捷键保存
editorRef.current.addCommand(
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.KeyS,
() => {
saveContent()
}
)
return () => {
ydoc.destroy()
provider.destroy()
}
}, [docId])
function handleEditorDidMount(editor: any, monaco: any) {
editorRef.current = editor
monacoRef.current = monaco
editor.updateOptions({
fontSize: 14,
fontFamily: 'JetBrains Mono, Consolas, monospace',
minimap: { enabled: false },
scrollBeyondLastLine: false,
lineNumbers: 'on',
renderWhitespace: 'boundary',
wordWrap: 'on',
})
}
return (
<div className="h-full relative">
<Editor
height="100%"
defaultLanguage="markdown"
// defaultValue={defaultValue}
theme="vs-light"
// onMount={handleEditorDidMount}
// onChange={handleEditorChange}
options={{
automaticLayout: true,
}}
/>
</div>
)
}
// 防抖函数
function debounce(func: Function, wait: number) {
let timeout: NodeJS.Timeout
return function executedFunction(...args: any[]) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}