初始化模版工程

This commit is contained in:
Cloud Bot
2026-03-20 07:33:46 +00:00
commit 23717e0ecd
386 changed files with 51675 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
import React, { useEffect, useRef } from 'react'
import { useDebounceFn } from 'ahooks'
import { cleanDom } from '../lib/core/utils'
import { Html } from '../components/html-render/task-html'
import { useIframeMode } from '../hooks/useIframeMode'
import { useDiff } from '../hooks/useDiff'
import { useEditState } from '../hooks/useEditState'
import { FloatingToolbar } from '../../ppt-editor/FloatingToolbar'
import type { ArtifactEditState } from '../types'
import { saveMarkdown } from '../server'
import { TaskArtifact } from '@/components/nova-sdk'
export const HtmlWithEditMode: React.FC<{
taskId: string
taskArtifact: TaskArtifact
onStateChange?: (state: ArtifactEditState) => void
content: string
isDoc?: boolean
}> = props => {
const { taskId, isDoc ,taskArtifact, content, onStateChange } = props
const htmlIframe = useRef<HTMLIFrameElement>(null)
const editState = useEditState()
const saveHandler = async (type: 'auto' | 'manual' = 'auto', callback?: () => void) => {
if (editState.isSaving) return
if (!htmlIframe.current) return
const iframeDoc = htmlIframe.current.contentDocument?.documentElement
if (!iframeDoc) return
editState.setSaveType(type)
editState.setIsSaving(true)
try {
const srcDoc = cleanDom(iframeDoc)
await saveMarkdown({
task_id: taskId,
content: srcDoc,
path: taskArtifact.path,
})
callback?.()
} catch (e) {
console.error(e)
} finally {
editState.setIsSaving(false)
editState.setSaveType(null)
}
}
const handleSave = useDebounceFn(saveHandler, { wait: 1000 })
const manualSave = () => {
handleSave.cancel()
saveHandler('manual')
}
const useIframeReturn = useIframeMode(taskId, htmlIframe, {
enableGlobalContentEditable: isDoc,
onContentChange: content => {
handleSave.run('auto')
},
onHistoryChange: (_state, instance) => {
editState.handleHistoryChangeEvent(instance)
// 同时向上传递状态,保持兼容性
if (onStateChange && instance) {
const canRedo = instance.EditorRegistry.canRedo()
const canUndo = instance.EditorRegistry.canUndo()
onStateChange({
canRedo,
canUndo,
redo: () => instance.EditorRegistry.redo(),
undo: () => instance.EditorRegistry.undo(),
})
}
},
})
useEffect(() => {
const saveHandler = (callback?: () => void) => {
if (!useIframeReturn.loadSuccess) {
callback?.()
return
}
if (!htmlIframe.current) return
const iframeDoc = htmlIframe.current.contentDocument?.documentElement
if (iframeDoc) {
const srcDoc = cleanDom(iframeDoc)
handleSave.run('auto', callback)
}
}
// eventBus.on('html-edit-save', saveHandler)
return () => {
// eventBus.off('html-edit-save', saveHandler)
}
}, [useIframeReturn.loadSuccess])
// doc编辑与web编辑差异逻辑
useDiff(useIframeReturn, isDoc)
return (
<div className="relative h-full ">
{/* 悬浮工具栏 */}
<Html
className={`opacity-0 ${'opacity-100'} h-full`}
content={content}
ref={htmlIframe}
/>
<div style={{ display: 'flex', justifyContent: 'center', padding: '12px 0 12px', zIndex: 100, position: 'absolute', left: 0, right: 0, top: 80 }}>
<FloatingToolbar
canUndo={editState.canUndo}
canRedo={editState.canRedo}
onUndo={() => editState.undo.current()}
onRedo={() => editState.redo.current()}
onSave={manualSave}
isSaving={editState.isSaving}
saveType={editState.saveType}
/>
</div>
</div>
)
}

View File

@@ -0,0 +1,39 @@
import React, { useRef } from 'react'
import { Html } from '../components/html-render/task-html'
import { HtmlWithEditMode } from './baseEdit'
import { PPTEditProvider } from '../context'
import { PPTEditToolBar } from '../components/toolbar-doc'
import type { TaskArtifact } from '@/components/nova-sdk/types'
import { useLoadContent } from '../hooks/useLoadContent'
import type { ArtifactEditState } from '../types'
const HtmlDoc = ({
taskId,
taskArtifact,
onStateChange,
editable = false,
}: {
taskId: string
taskArtifact: TaskArtifact
onStateChange?: (state: ArtifactEditState) => void
editable?: boolean
}) => {
const containerRef = useRef<HTMLDivElement>(null)
const content = useLoadContent(taskArtifact);
if (!editable) {
return <Html className='h-full' content={content} />
}
return (
<PPTEditProvider>
<div className='relative h-full'>
<PPTEditToolBar containerRef={containerRef} />
<div className='h-full' ref={containerRef}>
<HtmlWithEditMode taskId={taskId} taskArtifact={taskArtifact} content={content} isDoc={true} onStateChange={onStateChange}/>
</div>
</div>
</PPTEditProvider>
)
}
export const TaskHtmlDoc = React.memo(HtmlDoc)

View File

@@ -0,0 +1,39 @@
import React, { useRef } from 'react'
import { Html } from '../components/html-render/task-html'
import { PPTEditToolBar } from '../components/toolbar-web'
import { PPTEditProvider } from '../context'
import { HtmlWithEditMode } from './baseEdit'
import type { TaskArtifact } from '@/components/nova-sdk/types'
import { useLoadContent } from '../hooks/useLoadContent'
import type { ArtifactEditState } from '../types'
function HtmlWeb({
taskId,
taskArtifact,
onStateChange,
editable = false,
}: {
taskId: string
taskArtifact: TaskArtifact
onStateChange?: (state: ArtifactEditState) => void
editable?: boolean
}) {
const containerRef = useRef<HTMLDivElement>(null)
const content = useLoadContent(taskArtifact);
if (!editable) {
return <Html className='h-full' content={content} />
}
return (
<PPTEditProvider>
<div className='relative h-full'>
<PPTEditToolBar containerRef={containerRef} />
<div className='h-full' ref={containerRef}>
<HtmlWithEditMode taskId={taskId} taskArtifact={taskArtifact} content={content} isDoc={false} onStateChange={onStateChange}/>
</div>
</div>
</PPTEditProvider>
)
}
export const TaskHtmlWeb = React.memo(HtmlWeb)