初始化模版工程

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 { useIframeMode } from '@/components/html-editor/hooks/useIframeMode'
import type { TaskArtifact } from '@/components/nova-sdk'
import { useDebounceFn } from 'ahooks'
import { useEffect } from 'react'
import { savePPT } from '../server'
import { useEditState } from '@/components/html-editor/hooks/useEditState'
import { usePPTEditContext } from '@/components/html-editor/context'
interface SliderListItem {
content: string
file_name: string
file_type: string
id: string
path: string
title: string
}
export interface SlideJson {
outline: Array<{
id: string;
summary: string;
title: string;
}>;
project_dir: string;
slide_ids: string[];
slide_list: SliderListItem[]
}
export const usePPTEditor = (
id: string,
slideRef: React.RefObject<HTMLIFrameElement | null>,
scale: number,
slideJson: SlideJson | undefined,
artifact: TaskArtifact,
taskId: string | null | undefined,
editable: boolean
) => {
const editorContext = usePPTEditContext()
const editState = useEditState()
const saveHandler = async (type: 'auto' | 'manual' = 'auto', callback?: () => void) => {
if (editState.isSaving) return
const slideList = editorContext?.originalSlide.current || []
if (!slideList || slideList.length === 0) return
editState.setSaveType(type)
editState.setIsSaving(true)
try {
console.log('保存变更, ppt页数', slideList.length, artifact)
await savePPT({
task_id: taskId!,
slide_content: JSON.stringify({
...slideJson,
slide_list: slideList,
}),
slide_path: artifact.path,
})
} catch (e) {
console.error(e)
} finally {
editState.setIsSaving(false)
editState.setSaveType(null)
setTimeout(() => {
callback?.()
}, 500);
}
}
const handleSave = useDebounceFn(saveHandler, { wait: 1000 })
const manualSave = () => {
handleSave.cancel()
saveHandler('manual')
}
const useIframeReturn = useIframeMode(
id,
slideRef,
{
onContentChange: content => {
const originalSlide = editorContext?.originalSlide.current || []
if (originalSlide && originalSlide.length) {
const slide = originalSlide.find(s => s.id === id)
if (slide) {
console.log('内容变更写入', id)
slide.content = content
}
handleSave.run('auto');
}
},
onHistoryChange: (_state, instance) => {
editState.handleHistoryChangeEvent(instance)
},
enabled: editable
},
scale,
)
const { selectedElement, editor, tipPosition } = useIframeReturn
useEffect(() => {
const hasActive = editor?.EditorRegistry.hasActiveEditor()
const reWriteState = { ...useIframeReturn }
if (hasActive && selectedElement) {
// 有激活的实例且为当前的实例
editorContext?.setState(reWriteState)
} else if (!selectedElement && !hasActive) {
// 失焦后清空所有状态关闭tip
reWriteState.position = null
reWriteState.tipPosition = null
editorContext?.setState(reWriteState)
}
}, [selectedElement, editor, tipPosition])
return {
...editState,
handleSave,
manualSave,
}
}

View File

@@ -0,0 +1,32 @@
import { useState, useLayoutEffect } from 'react'
/**
* 监听元素尺寸变化的 Hook
*/
export function useSize(target: React.RefObject<HTMLElement | null>) {
const [size, setSize] = useState<{ width: number; height: number } | null>(null)
useLayoutEffect(() => {
const element = target.current
if (!element) {
console.log('useSize: element is null')
return
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect
console.log('useSize: resize', width, height)
setSize({ width, height })
}
})
resizeObserver.observe(element)
return () => {
resizeObserver.disconnect()
}
}, [target])
return size
}