提交 72492e6c authored 作者: 王鹏飞's avatar 王鹏飞

refactor: 重构数字人模块

上级 5452e724
...@@ -28,20 +28,20 @@ ...@@ -28,20 +28,20 @@
"axios": "^1.18.1", "axios": "^1.18.1",
"dayjs": "^1.11.11", "dayjs": "^1.11.11",
"easy-formula-editor": "^0.0.2-alpha.1", "easy-formula-editor": "^0.0.2-alpha.1",
"echarts": "^5.4.3", "echarts": "^6.1.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.8",
"js-md5": "^0.8.3", "js-md5": "^0.8.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.18.1",
"markdown-it": "^14.1.0", "markdown-it": "^14.3.0",
"mathjax": "3.2.2", "mathjax": "3.2.2",
"nanoid": "^3.3.8", "nanoid": "^3.3.18",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13", "react-error-boundary": "^4.0.13",
"react-router": "^7.18.1", "react-router": "^7.18.2",
"react-use": "^17.6.0", "react-use": "^17.6.0",
"snabbdom": "^3.6.2", "snabbdom": "^3.6.2",
"zustand": "^4.5.5" "zustand": "^4.5.5"
...@@ -52,14 +52,14 @@ ...@@ -52,14 +52,14 @@
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/react": "^18.3.7", "@types/react": "^18.3.7",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^6.0.3", "@vitejs/plugin-react": "^6.0.5",
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
"less": "^4.2.0", "less": "^4.2.0",
"oxfmt": "^0.59.0", "oxfmt": "^0.59.0",
"oxlint": "^1.74.0", "oxlint": "^1.74.0",
"typescript": "^7.0.2", "typescript": "^7.0.2",
"vite": "^8.1.5", "vite": "^8.2.1",
"vite-plugin-mkcert": "^1.17.10", "vite-plugin-mkcert": "^2.1.0",
"vitest": "^4.1.10" "vitest": "^4.1.10"
}, },
"browserslist": [ "browserslist": [
......
...@@ -72,9 +72,20 @@ export const upload = async (data, options = {}) => { ...@@ -72,9 +72,20 @@ export const upload = async (data, options = {}) => {
return response.result return response.result
} }
export const uploadDigitalHumanFile = async (file, providerType, options = {}) => {
const data = new FormData()
data.append('file', file)
const response = await request.post('/api/digitalhuman/open/v1/file/upload', data, {
params: { providerType, sourceFileName: file.name },
headers: { 'Content-Type': 'multipart/form-data' },
...options,
})
return response.result
}
// 获取数字人列表 // 获取数字人列表
export const getDigitalHumanList = async (params, options) => { export const getDigitalHumanList = async (params, options) => {
const { systemFigure, item = 'VIDEO-DH_2D', pageSize = 1000 } = params const { systemFigure, item = 'VIDEO-DH_2D_LITE', pageSize = 1000 } = params
const response = await request.get('/api/digitalhuman/open/v1/figure/query', { const response = await request.get('/api/digitalhuman/open/v1/figure/query', {
params: { systemFigure, item, pageSize }, params: { systemFigure, item, pageSize },
...options, ...options,
...@@ -82,6 +93,15 @@ export const getDigitalHumanList = async (params, options) => { ...@@ -82,6 +93,15 @@ export const getDigitalHumanList = async (params, options) => {
return response.result?.result || [] return response.result?.result || []
} }
// 获取系统音色列表
export const getDigitalHumanVoiceList = async (params = {}, options) => {
const response = await request.get('/api/digitalhuman/open/v1/tts/persons', {
params: { isSystem: true, ...params },
...options,
})
return response.result?.result || response.result || []
}
/** /**
* 提交生成式数字人形象定制任务 * 提交生成式数字人形象定制任务
*/ */
...@@ -130,3 +150,24 @@ export const submitVideoAdvanced = async (params, options = {}) => { ...@@ -130,3 +150,24 @@ export const submitVideoAdvanced = async (params, options = {}) => {
export const getVideoAdvancedTask = async (params, options = {}) => { export const getVideoAdvancedTask = async (params, options = {}) => {
return request.get('/api/digitalhuman/open/v1/video/advanced/task', { params, ...options }) return request.get('/api/digitalhuman/open/v1/video/advanced/task', { params, ...options })
} }
/**
* 校验照片数字人人像图片
*/
export const verifyImageVideo = async (params, options = {}) => {
return request.post('/api/digitalhuman/open/v1/video/image/verify', params, options)
}
/**
* 提交照片数字人视频任务
*/
export const submitImageVideo = async (params, options = {}) => {
return request.post('/api/digitalhuman/open/v1/video/image/submit', params, options)
}
/**
* 查询照片数字人视频任务
*/
export const getImageVideoTask = async (params, options = {}) => {
return request.get('/api/digitalhuman/open/v1/video/image/task', { params, ...options })
}
export const DIGITAL_HUMAN_TYPES = {
BASIC: 'basic',
ADVANCED: 'advanced',
FAST: 'fast',
PHOTO: 'photo',
}
export const DIGITAL_HUMAN_TYPE_LABELS = {
[DIGITAL_HUMAN_TYPES.BASIC]: '基础接口',
[DIGITAL_HUMAN_TYPES.ADVANCED]: '高级接口',
[DIGITAL_HUMAN_TYPES.FAST]: '已有真人视频',
[DIGITAL_HUMAN_TYPES.PHOTO]: '照片数字人',
}
// 视频组件只在这里切换接口,页面不再把接口能力暴露成四个并列选项。
export const DIGITAL_HUMAN_VIDEO_CONFIG = {
interfaceType: DIGITAL_HUMAN_TYPES.BASIC,
figureItem: 'VIDEO-DH_2D_LITE',
}
export function getConfiguredDigitalHumanType() {
return DIGITAL_HUMAN_VIDEO_CONFIG.interfaceType === DIGITAL_HUMAN_TYPES.ADVANCED
? DIGITAL_HUMAN_TYPES.ADVANCED
: DIGITAL_HUMAN_TYPES.BASIC
}
export const ADVANCED_TEMPLATES = [
{
value: 't-pf4kqasspwzwyexyte121',
label: '视频样式 1',
ratio: 'portrait',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/1_8c5a2e7.png',
},
{
value: 't-af4keqsspfzwyexyte123',
label: '视频样式 2',
ratio: 'portrait',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/2_755cd08.png',
},
{
value: 't-ad4eeqsspfzwyqxyte125',
label: '视频样式 3',
ratio: 'portrait',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/3_c730e57.png',
},
{
value: 't-cd4eeqsspfzwyqxyte127',
label: '视频样式 4',
ratio: 'portrait',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/4_d542bdf.png',
},
{
value: 't-hb4eeqsspfzwyqxyte129',
label: '视频样式 5',
ratio: 'landscape',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/5_5708421.png',
},
{
value: 't-cd4eeqsspfzwyqxyteditu3',
label: '视频样式 6',
ratio: 'portrait',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%20%284%29_a9ef435.png',
},
{
value: 't-cd4eeqsspfzwyqxyteditu4',
label: '视频样式 7',
ratio: 'portrait',
preview: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%20%285%29_4f18cab.png',
},
{
value: 't-cd4eeqsspfzwyqxyteditu5',
label: '视频样式 8',
ratio: 'portrait',
withoutFigure: true,
preview:
'https://bce.bdstatic.com/doc/bce-doc/AI_DH/%E6%97%A0%E6%95%B0%E5%AD%97%E4%BA%BA%E6%A8%A1%E6%9D%BF_97739d1.png',
},
]
function compact(value) {
if (Array.isArray(value)) return value.filter(Boolean)
if (!value || typeof value !== 'object') return value
return Object.fromEntries(
Object.entries(value)
.filter(([, item]) => item !== '' && item !== undefined && item !== null)
.map(([key, item]) => [key, compact(item)]),
)
}
function buildDriveParams(options, includeLanguage = true) {
if (options.driveType === 'VOICE') {
return {
driveType: 'VOICE',
inputAudioUrl: options.inputAudioUrl,
}
}
return {
driveType: 'TEXT',
text: options.text,
ttsParams: {
person: options.voiceId,
lan: includeLanguage ? options.language || undefined : undefined,
speed: String(options.speed ?? 5),
volume: String(options.volume ?? 5),
pitch: String(options.pitch ?? 5),
},
}
}
export function buildDigitalHumanRequest(type, options) {
const driveParams = buildDriveParams(options, type !== DIGITAL_HUMAN_TYPES.ADVANCED)
if (type === DIGITAL_HUMAN_TYPES.FAST) {
return compact({
templateVideoId: options.templateVideoId,
...driveParams,
})
}
if (type === DIGITAL_HUMAN_TYPES.PHOTO) {
return compact({
inputImageUrl: options.inputImageUrl,
model: options.model,
...driveParams,
subtitleParams: {
enabled: options.driveType === 'TEXT' && !!options.enableSubtitle,
subtitlePolicy: 'SRT',
},
})
}
if (type === DIGITAL_HUMAN_TYPES.ADVANCED) {
return compact({
figureId: options.figureId,
templateId: options.templateId,
model: options.model,
...driveParams,
title: options.title,
logoParams: options.logoUrl ? { logoUrl: options.logoUrl } : undefined,
bgmParams: options.bgmUrl ? { bgmUrl: options.bgmUrl } : undefined,
materialUrl: options.materialUrl,
extendBackgroundUrl: options.extendBackgroundUrl,
riskTip: options.riskTip,
openingMaterial: options.openingFileId
? { fileId: options.openingFileId, mediaType: 'VIDEO' }
: options.openingUrl
? { fileUrl: options.openingUrl, mediaType: 'VIDEO' }
: undefined,
endingMaterial: options.endingFileId
? { fileId: options.endingFileId, mediaType: 'VIDEO' }
: options.endingUrl
? { fileUrl: options.endingUrl, mediaType: 'VIDEO' }
: undefined,
mashupMaterials: options.mashupMaterials?.map(({ fileId, fileUrl, mediaType }) => ({
fileId,
fileUrl,
mediaType,
})),
videoParams: {
width: Number(options.width),
height: Number(options.height),
},
dhParams: options.dhParams,
fissionParams:
options.driveType === 'TEXT' && options.fissionFigureIds?.length
? {
figureIds: options.fissionFigureIds,
ttsPersons: options.fissionVoiceIds,
}
: undefined,
})
}
return compact({
figureId: options.figureId,
...driveParams,
backgroundImageUrl: options.transparent ? undefined : options.backgroundImageUrl,
playControlParams: options.enableSegmentLoop
? {
controlType: 'SEGMENT_LOOP',
startTimestamp: Number(options.startTimestamp || 0),
endTimestamp: Number(options.endTimestamp),
}
: undefined,
videoParams: {
width: Number(options.width),
height: Number(options.height),
transparent: !!options.transparent,
},
dhParams:
options.dhParams ||
(options.positionMode === 'camera'
? { cameraId: Number(options.cameraId) }
: options.positionMode === 'pixel'
? {
positionV2: {
crop: {
top: Number(options.cropTop),
left: Number(options.cropLeft),
width: Number(options.cropWidth),
height: Number(options.cropHeight),
},
location: {
top: Number(options.locationTop),
left: Number(options.locationLeft),
width: Number(options.locationWidth),
height: Number(options.locationHeight),
},
},
}
: options.positionX !== undefined
? {
position: {
x: Number(options.positionX),
y: Number(options.positionY),
z: Number(options.positionZ),
},
}
: undefined),
autoAnimoji: options.driveType === 'TEXT' && !!options.autoAnimoji,
subtitleParams: {
enabled: options.driveType === 'TEXT' && !!options.enableSubtitle,
subtitlePolicy: 'SRT',
},
})
}
const STORAGE_KEY = 'center-book:digital-human-tasks'
const MAX_TASKS = 10
function getStorage(storage) {
if (storage) return storage
if (typeof window === 'undefined') return null
return window.localStorage
}
export function readDigitalHumanTasks(storage) {
try {
const value = getStorage(storage)?.getItem(STORAGE_KEY)
const tasks = value ? JSON.parse(value) : []
return Array.isArray(tasks) ? tasks : []
} catch {
return []
}
}
export function saveDigitalHumanTask(task, storage) {
const target = getStorage(storage)
if (!target || !task?.taskId) return []
const tasks = readDigitalHumanTasks(target)
const nextTasks = [task, ...tasks.filter((item) => item.taskId !== task.taskId)].slice(
0,
MAX_TASKS,
)
try {
target.setItem(STORAGE_KEY, JSON.stringify(nextTasks))
} catch {
return tasks
}
return nextTasks
}
export function clearDigitalHumanTasks(storage) {
getStorage(storage)?.removeItem(STORAGE_KEY)
}
import { useState, useEffect, useRef, useCallback } from 'react' import { useState, useEffect, useRef, useCallback } from 'react'
import { message } from 'antd' import { message } from 'antd'
import { getDigitalHumanList, submitVideo, getVideoTask } from '@/components/editor/api/xiling' import {
getDigitalHumanList,
getDigitalHumanVoiceList,
getImageVideoTask,
getVideoAdvancedTask,
getVideoTask,
submitImageVideo,
submitVideo,
submitVideoAdvanced,
submitVideoFast,
} from '@/components/editor/api/xiling'
import { import {
digitalHumans as staticFigures, digitalHumans as staticFigures,
femaleVoices, femaleVoices as staticFemaleVoices,
maleVoices, maleVoices as staticMaleVoices,
} from '@/components/editor/wangeditor-customer/public-api' } from '@/components/editor/wangeditor-customer/public-api'
import {
readDigitalHumanTasks,
saveDigitalHumanTask,
} from '@/components/editor/hooks/digitalHumanTaskStore'
import {
buildDigitalHumanRequest,
DIGITAL_HUMAN_VIDEO_CONFIG,
DIGITAL_HUMAN_TYPES,
} from '@/components/editor/hooks/digitalHumanRequest'
const POLL_INTERVAL = 5000
const MAX_POLL_ERRORS = 3
const MAX_POLL_DURATION = 30 * 60 * 1000
/** function normalizeDigitalHumanVoice(voice) {
* 数字人功能 Hook (简化版:仅文本驱动,移除底图) const gender = String(voice.gender || '').toLowerCase()
*/ return {
id: voice.perId || voice.id,
name: voice.name,
gender: gender === 'male' ? '男声' : gender === 'female' ? '女声' : '未知音色',
style: voice.describe || voice.style || '',
previewUrl: voice.exampleAudioUrl || voice.previewUrl,
version: voice.version,
systemProvided: voice.systemProvided,
}
}
function queryVideoTask(task) {
const params = { taskId: task.taskId }
if (task.type === DIGITAL_HUMAN_TYPES.ADVANCED) return getVideoAdvancedTask(params)
if (task.type === DIGITAL_HUMAN_TYPES.PHOTO) return getImageVideoTask(params)
return getVideoTask(params)
}
function submitVideoTask(type, params) {
if (type === DIGITAL_HUMAN_TYPES.ADVANCED) return submitVideoAdvanced(params)
if (type === DIGITAL_HUMAN_TYPES.FAST) return submitVideoFast(params)
if (type === DIGITAL_HUMAN_TYPES.PHOTO) return submitImageVideo(params)
return submitVideo(params)
}
/** 数字人视频任务:提交、轮询、恢复与重试。 */
export default function useDigitalHuman() { export default function useDigitalHuman() {
const [figures, setFigures] = useState(staticFigures) const [figures, setFigures] = useState([])
const [femaleVoices, setFemaleVoices] = useState([])
const [maleVoices, setMaleVoices] = useState([])
const [loadingFigures, setLoadingFigures] = useState(false) const [loadingFigures, setLoadingFigures] = useState(false)
const [isGenerating, setIsGenerating] = useState(false) const [isGenerating, setIsGenerating] = useState(false)
const [generatedVideo, setGeneratedVideo] = useState(null) const [generatedVideo, setGeneratedVideo] = useState(null)
const [error, setError] = useState(null) const [error, setError] = useState(null)
const [tasks, setTasks] = useState(readDigitalHumanTasks)
const [activeTask, setActiveTask] = useState(null)
const pollTimerRef = useRef(null) const pollTimerRef = useRef(null)
const pollingTaskIdRef = useRef(null)
const stopPolling = useCallback(() => { const stopPolling = useCallback(() => {
if (pollTimerRef.current) { if (pollTimerRef.current) {
clearInterval(pollTimerRef.current) clearTimeout(pollTimerRef.current)
pollTimerRef.current = null pollTimerRef.current = null
} }
pollingTaskIdRef.current = null
}, [])
const persistTask = useCallback((task) => {
setTasks(saveDigitalHumanTask(task))
setActiveTask(task)
return task
}, []) }, [])
const startPolling = useCallback( const startPolling = useCallback(
(taskId, figureName) => { (task) => {
stopPolling() stopPolling()
pollTimerRef.current = setInterval(async () => { pollingTaskIdRef.current = task.taskId
setIsGenerating(true)
setError(null)
let pollErrors = 0
const startedAt = Date.now()
const poll = async () => {
if (pollingTaskIdRef.current !== task.taskId) return
try { try {
const response = await getVideoTask({ taskId }) const response = await queryVideoTask(task)
const { status, videoUrl } = response.result const result = response.result || {}
const { status, videoUrl } = result
pollErrors = 0
if (status === 'SUCCESS' && videoUrl) { if (status === 'SUCCESS' && videoUrl) {
stopPolling() stopPolling()
setGeneratedVideo({ const completedTask = persistTask({
...task,
...result,
status: 'SUCCESS',
url: videoUrl, url: videoUrl,
taskId: taskId, updatedAt: Date.now(),
figureName: figureName,
}) })
setGeneratedVideo(completedTask)
setIsGenerating(false) setIsGenerating(false)
message.success('数字人视频生成成功!') message.success('数字人视频生成成功')
} else if (status === 'FAILED') { } else if (status === 'FAILED') {
stopPolling() stopPolling()
const failedMessage = result.failedMessage || '生成失败'
persistTask({
...task,
...result,
status: 'FAILED',
error: failedMessage,
updatedAt: Date.now(),
})
setIsGenerating(false) setIsGenerating(false)
setError(response.result.failedMessage || '生成失败') setError(failedMessage)
message.error(`视频生成失败: ${response.result.failedMessage || '未知错误'}`) message.error(`视频生成失败:${failedMessage}`)
} else if (Date.now() - startedAt >= MAX_POLL_DURATION) {
stopPolling()
const timeoutMessage = '查询已暂停,稍后可继续查询任务状态'
persistTask({
...task,
...result,
status: 'PAUSED',
error: timeoutMessage,
updatedAt: Date.now(),
})
setIsGenerating(false)
setError(timeoutMessage)
} else {
const processingTask = {
...task,
...result,
status: 'PROCESSING',
updatedAt: Date.now(),
}
persistTask(processingTask)
pollTimerRef.current = setTimeout(poll, POLL_INTERVAL)
} }
} catch (err) { } catch (err) {
console.error('Polling Error:', err) console.error('Polling Error:', err)
pollErrors += 1
if (pollErrors >= MAX_POLL_ERRORS) {
stopPolling()
const networkMessage = '网络异常,已暂停查询,可稍后继续'
persistTask({
...task,
status: 'PAUSED',
error: networkMessage,
updatedAt: Date.now(),
})
setIsGenerating(false)
setError(networkMessage)
} else {
pollTimerRef.current = setTimeout(poll, POLL_INTERVAL)
} }
}, 5000) }
}
poll()
}, },
[stopPolling], [persistTask, stopPolling],
) )
// 处理获取列表 // 一次加载公共和已定制形象,弹框内仅负责滚动浏览。
const fetchFigures = useCallback(async () => { const fetchFigures = useCallback(async () => {
setLoadingFigures(true) setLoadingFigures(true)
try { try {
const list = await getDigitalHumanList({ systemFigure: true }) const list = await getDigitalHumanList({
if (list && list.length > 0) { item: DIGITAL_HUMAN_VIDEO_CONFIG.figureItem,
setFigures((prev) => { pageSize: 1000,
// 创建新数组,保留静态配置,并用 API 的数据补充或更新关键信息(如 avatar) })
const combined = [...prev]
list.forEach((item) => {
const existingIndex = combined.findIndex((c) => String(c.id) === String(item.figureId))
const avatarUrl = item.figureImageUrl || item.figureVideoThumbnailUrl
if (existingIndex > -1) { if (list && list.length > 0) {
// 如果静态配置已存在,且 API 返回了有效的预览图,则更新它 setFigures(
if (avatarUrl) { list.map((item) => ({
combined[existingIndex] = {
...combined[existingIndex],
avatar: avatarUrl,
}
}
} else {
// 如果是全新的数字人,则添加
combined.push({
id: item.figureId, id: item.figureId,
name: item.name, name: item.name,
avatar: avatarUrl, avatar: item.figureImageUrl || item.figureVideoThumbnailUrl || item.templateImg,
}) gender: item.gender,
} tags: item.tags,
}) resolutionWidth: item.resolutionWidth,
return combined resolutionHeight: item.resolutionHeight,
}) pose: item.pose,
background: item.background,
age: item.age,
templateVideoUrl: item.templateVideoUrl,
})),
)
} else {
setFigures(staticFigures)
} }
} catch (err) { } catch (err) {
console.error('Fetch Figures Error:', err) console.error('Fetch Figures Error:', err)
setFigures(staticFigures)
} finally { } finally {
setLoadingFigures(false) setLoadingFigures(false)
} }
...@@ -99,61 +215,89 @@ export default function useDigitalHuman() { ...@@ -99,61 +215,89 @@ export default function useDigitalHuman() {
const generateVideo = useCallback( const generateVideo = useCallback(
async (options) => { async (options) => {
const { const type = options.type || DIGITAL_HUMAN_TYPES.BASIC
figureId,
figureName,
text,
voiceId,
width = 720,
height = 1280,
transparent = true,
enableSubtitle = false,
backgroundImageUrl,
} = options
setIsGenerating(true) setIsGenerating(true)
setGeneratedVideo(null) setGeneratedVideo(null)
setError(null) setError(null)
try { try {
const params = { const params = buildDigitalHumanRequest(type, options)
figureId,
driveType: 'TEXT',
text,
...(backgroundImageUrl ? { backgroundImageUrl } : {}),
ttsParams: {
person: voiceId || '5116',
speed: '5',
pitch: '5',
volume: '5',
},
videoParams: {
width: parseInt(width),
height: parseInt(height),
transparent: !!transparent,
},
subtitleParams: {
enabled: !!enableSubtitle,
subtitlePolicy: 'SRT',
},
}
const response = await submitVideo(params) const response = await submitVideoTask(type, params)
if (response.result && response.result.taskId) { const result = response.result || {}
startPolling(response.result.taskId, figureName) const submittedTasks = result.taskId
return response.result.taskId ? [result]
: (result.fissionTasks || []).filter((item) => item.code === 0)
if (submittedTasks.length > 0) {
const createdAt = Date.now()
const tasks = submittedTasks.map((item) =>
persistTask({
taskId: item.taskId,
type,
status: 'PROCESSING',
figureName: options.figureName || item.figureId || '数字人',
createdAt,
updatedAt: createdAt,
options: {
...options,
figureId: item.figureId || options.figureId,
voiceId: item.ttsPerson || options.voiceId,
},
}),
)
startPolling(tasks[0])
return tasks[0].taskId
} else { } else {
throw new Error('TaskId missing') throw new Error('未获取到任务编号,请稍后重试')
} }
} catch (err) { } catch (err) {
setIsGenerating(false) setIsGenerating(false)
const msg = err.response?.data?.message?.global || '提交失败' const msg =
err.response?.data?.message?.global ||
err.data?.message?.global ||
err.message ||
'提交失败'
setError(msg) setError(msg)
message.error(msg) message.error(msg)
throw err throw err
} }
}, },
[startPolling], [persistTask, startPolling],
)
const selectTask = useCallback(
(task) => {
if (task.status === 'PROCESSING') {
setGeneratedVideo(null)
startPolling(task)
return
}
stopPolling()
setActiveTask(task)
setError(task.error || null)
setGeneratedVideo(task.status === 'SUCCESS' && task.url ? task : null)
setIsGenerating(false)
},
[startPolling, stopPolling],
)
const resumeTask = useCallback(
(task = activeTask) => {
if (!task?.taskId) return
setGeneratedVideo(null)
startPolling({ ...task, status: 'PROCESSING', error: null })
},
[activeTask, startPolling],
)
const retryTask = useCallback(
(task = activeTask) => {
if (!task?.options) return Promise.reject(new Error('缺少原始生成参数'))
return generateVideo(task.options)
},
[activeTask, generateVideo],
) )
useEffect(() => { useEffect(() => {
...@@ -161,6 +305,50 @@ export default function useDigitalHuman() { ...@@ -161,6 +305,50 @@ export default function useDigitalHuman() {
return () => stopPolling() return () => stopPolling()
}, [fetchFigures, stopPolling]) }, [fetchFigures, stopPolling])
useEffect(() => {
let active = true
getDigitalHumanVoiceList()
.then((list) => {
if (!active) return
const voices = (Array.isArray(list) ? list : [])
.map(normalizeDigitalHumanVoice)
.filter((voice) => voice.id)
.map((voice) => {
if (voice.previewUrl) return voice
// 系统音色的 exampleAudioUrl 可能为空,按 perId 回退到静态试听地址
const fallback = [...staticFemaleVoices, ...staticMaleVoices].find(
(item) => String(item.id) === String(voice.id),
)
return fallback ? { ...voice, previewUrl: fallback.previewUrl } : voice
})
if (voices.length === 0) {
setFemaleVoices(staticFemaleVoices)
setMaleVoices(staticMaleVoices)
return
}
setFemaleVoices(voices.filter((voice) => voice.gender === '女声'))
setMaleVoices(voices.filter((voice) => voice.gender === '男声'))
})
.catch((err) => {
console.error('Fetch Voices Error:', err)
if (active) {
setFemaleVoices(staticFemaleVoices)
setMaleVoices(staticMaleVoices)
}
})
return () => {
active = false
}
}, [])
useEffect(() => {
const unfinishedTask = readDigitalHumanTasks().find((task) => task.status === 'PROCESSING')
if (unfinishedTask) startPolling(unfinishedTask)
}, [startPolling])
return { return {
figures, figures,
femaleVoices, femaleVoices,
...@@ -169,7 +357,13 @@ export default function useDigitalHuman() { ...@@ -169,7 +357,13 @@ export default function useDigitalHuman() {
isGenerating, isGenerating,
generatedVideo, generatedVideo,
error, error,
tasks,
activeTask,
generateVideo, generateVideo,
selectTask,
resumeTask,
retryTask,
refreshFigures: fetchFigures,
resetGeneratedVideo: () => setGeneratedVideo(null), resetGeneratedVideo: () => setGeneratedVideo(null),
} }
} }
import { describe, expect, it } from 'vitest'
import { buildDigitalHumanRequest, DIGITAL_HUMAN_TYPES } from '../../hooks/digitalHumanRequest'
const common = {
driveType: 'TEXT',
text: '数字人测试。',
voiceId: '5116',
language: 'Chinese',
speed: 5,
volume: 6,
pitch: 4,
}
describe('digital human request builder', () => {
it('builds the basic video contract', () => {
expect(
buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.BASIC, {
...common,
figureId: '1081',
width: 720,
height: 1280,
transparent: false,
enableSubtitle: true,
positionX: 0,
positionY: 0.2,
positionZ: 1,
}),
).toMatchObject({
figureId: '1081',
driveType: 'TEXT',
ttsParams: { person: '5116', speed: '5', volume: '6', pitch: '4' },
videoParams: { width: 720, height: 1280, transparent: false },
dhParams: { position: { x: 0, y: 0.2, z: 1 } },
subtitleParams: { enabled: true, subtitlePolicy: 'SRT' },
})
})
it('builds the advanced video media and fission contract', () => {
const result = buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.ADVANCED, {
...common,
figureId: '1081',
templateId: 'template-1',
width: 1080,
height: 1920,
model: 'turbo_v2',
title: '标题',
logoUrl: 'https://example.com/logo.png',
bgmUrl: 'https://example.com/bgm.mp3',
materialUrl: 'https://example.com/material.mp4',
extendBackgroundUrl: 'https://example.com/extend.png',
riskTip: '示例水印',
openingFileId: 'opening-file-id',
endingFileId: 'ending-file-id',
mashupMaterials: [
{ fileId: 'image-file-id', fileName: '背景.jpg', mediaType: 'IMAGE' },
{ fileId: 'video-file-id', fileName: '背景.mp4', mediaType: 'VIDEO' },
],
fissionFigureIds: ['1081', '1112'],
fissionVoiceIds: ['5116'],
})
expect(result).toMatchObject({
figureId: '1081',
templateId: 'template-1',
model: 'turbo_v2',
title: '标题',
logoParams: { logoUrl: 'https://example.com/logo.png' },
bgmParams: { bgmUrl: 'https://example.com/bgm.mp3' },
materialUrl: 'https://example.com/material.mp4',
extendBackgroundUrl: 'https://example.com/extend.png',
riskTip: '示例水印',
openingMaterial: { fileId: 'opening-file-id', mediaType: 'VIDEO' },
endingMaterial: { fileId: 'ending-file-id', mediaType: 'VIDEO' },
mashupMaterials: [
{ fileId: 'image-file-id', mediaType: 'IMAGE' },
{ fileId: 'video-file-id', mediaType: 'VIDEO' },
],
fissionParams: { figureIds: ['1081', '1112'], ttsPersons: ['5116'] },
})
expect(result).not.toHaveProperty('ttsParams.lan')
})
it('does not enable fission without figure IDs', () => {
const result = buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.ADVANCED, {
...common,
figureId: '1081',
templateId: 'template-1',
width: 1080,
height: 1920,
fissionVoiceIds: ['5116'],
})
expect(result).not.toHaveProperty('fissionParams')
})
it('supports the basic camera and pixel positioning contracts', () => {
expect(
buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.BASIC, {
...common,
figureId: '1081',
width: 1080,
height: 1920,
positionMode: 'camera',
cameraId: 3,
}),
).toMatchObject({ dhParams: { cameraId: 3 } })
expect(
buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.BASIC, {
...common,
figureId: '1081',
width: 1080,
height: 1920,
positionMode: 'pixel',
cropTop: 10,
cropLeft: 20,
cropWidth: 900,
cropHeight: 1600,
locationTop: 30,
locationLeft: 40,
locationWidth: 720,
locationHeight: 1280,
}),
).toMatchObject({
dhParams: {
positionV2: {
crop: { top: 10, left: 20, width: 900, height: 1600 },
location: { top: 30, left: 40, width: 720, height: 1280 },
},
},
})
})
it('uses the visual preview location for 2D small-sample figures', () => {
expect(
buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.BASIC, {
...common,
figureId: '1081',
width: 1920,
height: 1080,
dhParams: {
positionV2: {
location: { top: -39, left: 525, width: 1920, height: 1080 },
},
},
}),
).toMatchObject({
dhParams: {
positionV2: {
location: { top: -39, left: 525, width: 1920, height: 1080 },
},
},
})
})
it('builds 123 and photo video contracts with their own inputs', () => {
expect(
buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.FAST, {
...common,
templateVideoId: 'https://example.com/123.mp4',
}),
).toMatchObject({ templateVideoId: 'https://example.com/123.mp4', driveType: 'TEXT' })
expect(
buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.PHOTO, {
...common,
inputImageUrl: 'https://example.com/photo.jpg',
model: 'turbo_v2',
enableSubtitle: true,
}),
).toMatchObject({
inputImageUrl: 'https://example.com/photo.jpg',
model: 'turbo_v2',
subtitleParams: { enabled: true },
})
})
it('uses input audio instead of TTS parameters for voice drive', () => {
const result = buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.PHOTO, {
driveType: 'VOICE',
inputAudioUrl: 'https://example.com/voice.mp3',
inputImageUrl: 'https://example.com/photo.jpg',
})
expect(result).toMatchObject({
driveType: 'VOICE',
inputAudioUrl: expect.any(String),
subtitleParams: { enabled: false },
})
expect(result).not.toHaveProperty('ttsParams')
})
it('omits options that do not take effect in the selected mode', () => {
const result = buildDigitalHumanRequest(DIGITAL_HUMAN_TYPES.BASIC, {
driveType: 'VOICE',
figureId: '1081',
inputAudioUrl: 'https://example.com/voice.mp3',
width: 1080,
height: 1920,
transparent: true,
backgroundImageUrl: 'https://example.com/background.jpg',
autoAnimoji: true,
enableSubtitle: true,
})
expect(result).not.toHaveProperty('backgroundImageUrl')
expect(result).toMatchObject({
autoAnimoji: false,
subtitleParams: { enabled: false },
})
})
})
import { describe, expect, it } from 'vitest'
import {
clearDigitalHumanTasks,
readDigitalHumanTasks,
saveDigitalHumanTask,
} from '../../hooks/digitalHumanTaskStore'
function createStorage() {
const values = new Map<string, string>()
return {
getItem: (key: string) => values.get(key) ?? null,
setItem: (key: string, value: string) => values.set(key, value),
removeItem: (key: string) => values.delete(key),
}
}
describe('digital human task storage', () => {
it('keeps the newest version of a task first', () => {
const storage = createStorage()
saveDigitalHumanTask({ taskId: 'task-1', status: 'PROCESSING' }, storage)
saveDigitalHumanTask({ taskId: 'task-2', status: 'SUCCESS' }, storage)
saveDigitalHumanTask({ taskId: 'task-1', status: 'FAILED' }, storage)
expect(readDigitalHumanTasks(storage)).toEqual([
{ taskId: 'task-1', status: 'FAILED' },
{ taskId: 'task-2', status: 'SUCCESS' },
])
})
it('keeps at most ten recent tasks and can clear them', () => {
const storage = createStorage()
for (let index = 0; index < 12; index += 1) {
saveDigitalHumanTask({ taskId: `task-${index}`, status: 'SUCCESS' }, storage)
}
expect(readDigitalHumanTasks(storage)).toHaveLength(10)
expect(readDigitalHumanTasks(storage)[0].taskId).toBe('task-11')
clearDigitalHumanTasks(storage)
expect(readDigitalHumanTasks(storage)).toEqual([])
})
it('ignores invalid stored data', () => {
const storage = createStorage()
storage.setItem('center-book:digital-human-tasks', '{invalid')
expect(readDigitalHumanTasks(storage)).toEqual([])
})
it('does not break generation when browser storage is unavailable', () => {
const storage = createStorage()
storage.setItem = () => {
throw new Error('storage unavailable')
}
expect(saveDigitalHumanTask({ taskId: 'task-1', status: 'PROCESSING' }, storage)).toEqual([])
})
})
import { useState } from 'react'
import { App, Button, Upload } from 'antd'
import { DeleteOutlined, UploadOutlined } from '@ant-design/icons'
import { uploadDigitalHumanFile } from '@/components/editor/api/xiling'
const PROVIDER_TYPE = 'OPEN_VIDEO_DURATION_VIDEO'
const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'bmp']
function getExtension(file) {
return file.name.split('.').pop()?.toLowerCase() || ''
}
function getMediaType(file) {
return file.type.startsWith('image/') || IMAGE_EXTENSIONS.includes(getExtension(file))
? 'IMAGE'
: 'VIDEO'
}
function matchesAccept(file, accept) {
const extension = getExtension(file)
return accept.split(',').some((item) => {
const pattern = item.trim().toLowerCase()
if (pattern.startsWith('.')) return pattern === `.${extension}`
if (pattern.endsWith('/*')) return file.type.startsWith(pattern.slice(0, -1))
return file.type.toLowerCase() === pattern
})
}
function getFileLimit(file) {
return getMediaType(file) === 'IMAGE' ? 3 : 500
}
export default function DigitalHumanBaiduUploadField({
label,
value,
onChange,
accept,
multiple = false,
maxCount = 1,
hint,
}) {
const { message } = App.useApp()
const [pendingUploads, setPendingUploads] = useState(0)
const uploading = pendingUploads > 0
const files = multiple ? value || [] : value ? [value] : []
const uploadDisabled = uploading || (multiple && files.length >= maxCount)
const upload = async ({ file, onSuccess, onError }) => {
if (!matchesAccept(file, accept)) {
const error = new Error('文件格式不符合要求')
message.error(error.message)
onError(error)
return
}
const limit = getFileLimit(file)
if (file.size > limit * 1024 * 1024) {
const error = new Error(
`${getMediaType(file) === 'IMAGE' ? '图片' : '视频'}不能超过 ${limit}MB`,
)
message.error(error.message)
onError(error)
return
}
setPendingUploads((current) => current + 1)
try {
const result = await uploadDigitalHumanFile(file, PROVIDER_TYPE)
if (!result?.fileId) throw new Error('上传结果缺少文件编号')
const nextFile = {
fileId: result.fileId,
fileName: result.fileName || file.name,
mediaType: getMediaType(file),
}
onChange(multiple ? (current) => [...(current || []), nextFile].slice(0, maxCount) : nextFile)
onSuccess(result)
message.success(`${label}上传成功`)
} catch (error) {
message.error(`${label}上传失败`)
onError(error)
} finally {
setPendingUploads((current) => Math.max(0, current - 1))
}
}
const remove = (index) => {
if (multiple) onChange(files.filter((_, itemIndex) => itemIndex !== index))
else onChange(null)
}
return (
<div className='media-field baidu-upload-field'>
<div className='field-label'>{label}</div>
<Upload
accept={accept}
multiple={multiple}
maxCount={maxCount}
showUploadList={false}
customRequest={upload}
disabled={uploadDisabled}
>
<Button icon={<UploadOutlined />} loading={uploading} disabled={uploadDisabled}>
{multiple
? `添加${label}(${files.length}/${maxCount})`
: files.length
? `更换${label}`
: `上传${label}`}
</Button>
</Upload>
{files.length ? (
<div className='baidu-upload-list'>
{files.map((file, index) => (
<div className='baidu-upload-item' key={file.fileId}>
<span>{file.fileName}</span>
<Button
type='text'
size='small'
icon={<DeleteOutlined />}
aria-label={`删除${file.fileName}`}
onClick={() => remove(index)}
/>
</div>
))}
</div>
) : null}
{hint ? <div className='field-hint'>{hint}</div> : null}
</div>
)
}
import { useState } from 'react'
import { Alert, App, Button, Form, Input, Radio, Tag } from 'antd'
import { ReloadOutlined } from '@ant-design/icons'
import { queryGenerativeFigure, submitGenerativeFigure } from '@/components/editor/api/xiling'
import DigitalHumanUploadField from './DigitalHumanUploadField'
const customizeStatus = {
LINE_UP: { color: 'default', text: '排队中' },
ACTIONING: { color: 'processing', text: '动作生成中' },
GENERATING: { color: 'processing', text: '训练中' },
SUCCESS: { color: 'success', text: '已完成' },
FAILED: { color: 'error', text: '定制失败' },
}
function findFigure(response, figureId) {
const payload = response?.result
const list = Array.isArray(payload?.result)
? payload.result
: Array.isArray(payload)
? payload
: payload
? [payload]
: []
return list.find((item) => String(item.figureId) === String(figureId)) || list[0]
}
export default function DigitalHumanCustomizePanel({ onSuccess }) {
const { message } = App.useApp()
const [imageUrl, setImageUrl] = useState('')
const [task, setTask] = useState(null)
const [submitting, setSubmitting] = useState(false)
const [querying, setQuerying] = useState(false)
const [error, setError] = useState('')
const handleSubmit = async (values) => {
if (!imageUrl) return message.warning('请先上传参考图片')
setSubmitting(true)
setError('')
try {
const response = await submitGenerativeFigure({
name: values.name.trim(),
customizeType: 'GENERATE_NO_ACTION',
gender: values.gender,
figureImageUrl: imageUrl,
})
const figureId = response.result?.figureId
if (!figureId) throw new Error('未获取到形象 ID')
setTask({ figureId, name: values.name.trim(), status: 'LINE_UP' })
message.success('形象定制任务已提交')
} catch (submitError) {
setError(submitError.response?.data?.message?.global || submitError.message || '提交失败')
} finally {
setSubmitting(false)
}
}
const handleQuery = async () => {
if (!task?.figureId) return
setQuerying(true)
setError('')
try {
const response = await queryGenerativeFigure({ figureId: task.figureId })
const figure = findFigure(response, task.figureId)
if (!figure) throw new Error('暂未查询到形象任务')
const nextTask = { ...task, ...figure }
setTask(nextTask)
if (nextTask.status === 'SUCCESS') onSuccess?.()
} catch (queryError) {
setError(queryError.response?.data?.message?.global || queryError.message || '查询失败')
} finally {
setQuerying(false)
}
}
return (
<div className='customize-layout'>
<section className='customize-form-card'>
<h2>生成式数字人形象定制</h2>
<p>上传一张人物图片,生成可用于视频合成的数字人形象。</p>
<Form
layout='vertical'
initialValues={{ gender: 'MALE' }}
requiredMark={false}
onFinish={handleSubmit}
>
<Form.Item
label='形象名称'
name='name'
rules={[
{ required: true, whitespace: true, message: '请输入形象名称' },
{ max: 20, message: '形象名称不能超过20个字符' },
]}
>
<Input maxLength={20} placeholder='请输入形象名称' />
</Form.Item>
<Form.Item label='性别' name='gender'>
<Radio.Group
optionType='button'
buttonStyle='solid'
options={[
{ label: '男', value: 'MALE' },
{ label: '女', value: 'FEMALE' },
]}
/>
</Form.Item>
<DigitalHumanUploadField
label='参考图片'
value={imageUrl}
onChange={setImageUrl}
accept='image/jpeg,image/png'
maxSizeMB={10}
filenamePrefix='digital-human-figure'
previewType='image'
hint='支持 JPG、JPEG、PNG,建议使用 1080×1920 或 1920×1080 图片。'
/>
<Button type='primary' htmlType='submit' loading={submitting} block>
开始定制
</Button>
</Form>
</section>
<section className='customize-result-card'>
<div className='customize-result-heading'>
<h2>定制进度</h2>
{task ? (
<Button icon={<ReloadOutlined />} onClick={handleQuery} loading={querying}>
查询进度
</Button>
) : null}
</div>
{error ? <Alert type='error' showIcon title={error} /> : null}
{task ? (
<div className='customize-task'>
{task.templateImg || task.pictureUrl || imageUrl ? (
<img
src={task.templateImg || task.pictureUrl || imageUrl}
alt={`${task.name || '数字人'}形象预览`}
/>
) : null}
<div>
<strong>{task.name || '数字人形象'}</strong>
<span>ID:{task.figureId}</span>
<Tag color={customizeStatus[task.status]?.color}>
{customizeStatus[task.status]?.text || task.status}
</Tag>
{task.failedMessage ? <small>{task.failedMessage}</small> : null}
</div>
</div>
) : (
<div className='customize-empty'>提交任务后,可在这里查询训练进度。</div>
)}
</section>
</div>
)
}
import { useState, useRef, useEffect } from 'react' import { useState, useRef, useEffect, useMemo } from 'react'
import { import {
Modal, Modal,
Input, Input,
InputNumber,
Button, Button,
message, message,
Spin, Spin,
Row, Select,
Col,
Space, Space,
Divider,
Checkbox,
Tabs,
Empty, Empty,
Alert,
Tag,
Collapse,
Slider,
Tabs,
} from 'antd' } from 'antd'
import { import {
VideoCameraOutlined,
CheckOutlined, CheckOutlined,
CopyOutlined,
DownloadOutlined, DownloadOutlined,
PlayCircleOutlined,
PauseCircleOutlined, PauseCircleOutlined,
PlayCircleOutlined,
ReloadOutlined,
SwapOutlined,
UserOutlined, UserOutlined,
} from '@ant-design/icons' } from '@ant-design/icons'
import { SlateTransforms } from '@wangeditor/editor' import { SlateTransforms } from '@wangeditor/editor'
import { saveAs } from 'file-saver' import { saveAs } from 'file-saver'
import { uploadFileByUrl } from '@/utils/oss' import { uploadFileByUrl } from '@/utils/oss'
import useDigitalHuman from '@/components/editor/hooks/useDigitalHuman' import useDigitalHuman from '@/components/editor/hooks/useDigitalHuman'
import {
ADVANCED_TEMPLATES,
DIGITAL_HUMAN_TYPES,
DIGITAL_HUMAN_TYPE_LABELS,
getConfiguredDigitalHumanType,
} from '@/components/editor/hooks/digitalHumanRequest'
import DigitalHumanUploadField from './DigitalHumanUploadField'
import DigitalHumanCustomizePanel from './DigitalHumanCustomizePanel'
import DigitalHumanBaiduUploadField from './DigitalHumanBaiduUploadField'
import './digital-human.less' import './digital-human.less'
import { import {
insertBlockNodesAndFocus, insertBlockNodesAndFocus,
...@@ -32,18 +45,121 @@ import { ...@@ -32,18 +45,121 @@ import {
} from '../../shared/editor-operations' } from '../../shared/editor-operations'
const { TextArea } = Input const { TextArea } = Input
const videoSizes = {
portrait: { width: 1080, height: 1920 },
landscape: { width: 1920, height: 1080 },
}
function getFigureVideoSize(figure) {
const width = Number(figure?.resolutionWidth)
const height = Number(figure?.resolutionHeight)
if (!width || !height) return null
return width >= height ? 'landscape' : 'portrait'
}
function getFigureLayerSize(canvasSize, figure, imageAspect = null) {
const figureWidth = Number(figure?.resolutionWidth)
const figureHeight = Number(figure?.resolutionHeight)
// 宽高比优先取接口分辨率;没有时取图片实际加载出的宽高比(imageAspect = 高/宽)
const figureRatio =
figureWidth && figureHeight ? figureWidth / figureHeight : imageAspect ? 1 / imageAspect : null
if (!figureRatio) return canvasSize
const canvasRatio = canvasSize.width / canvasSize.height
if (figureRatio < canvasRatio) {
return {
width: Math.max(2, Math.round(canvasSize.height * figureRatio)),
height: canvasSize.height,
}
}
return {
width: canvasSize.width,
height: Math.max(2, Math.round(canvasSize.width / figureRatio)),
}
}
function buildPreviewDhParams(size, layout, figure, imageAspect = null) {
const layerSize = getFigureLayerSize(size, figure, imageAspect)
const width = Math.max(2, Math.round((layerSize.width * layout.scale) / 2) * 2)
const height = Math.max(2, Math.round((layerSize.height * layout.scale) / 2) * 2)
return {
positionV2: {
location: {
top: Math.round((size.height * layout.y) / 100 - height / 2),
left: Math.round((size.width * layout.x) / 100 - width / 2),
width,
height,
},
},
}
}
const DEFAULT_MODEL = 'quality_v2'
const taskStatus = {
PROCESSING: { color: 'processing', text: '生成中' },
SUCCESS: { color: 'success', text: '已完成' },
FAILED: { color: 'error', text: '生成失败' },
PAUSED: { color: 'warning', text: '查询已暂停' },
}
const CUSTOMIZE_WORKSPACE = 'customize'
const workspaceTabs = [
{ key: DIGITAL_HUMAN_TYPES.BASIC, label: '基础视频合成' },
{ key: DIGITAL_HUMAN_TYPES.ADVANCED, label: '高级视频合成' },
{ key: CUSTOMIZE_WORKSPACE, label: '形象定制' },
]
export default function AIDigitalHumanModal(props) { export default function AIDigitalHumanModal(props) {
const { editor } = props const { editor } = props
const [isModalOpen, setIsModalOpen] = useState(true) const [isModalOpen, setIsModalOpen] = useState(true)
// 核心选择状态
const [selectedFigure, setSelectedFigure] = useState(null) const [selectedFigure, setSelectedFigure] = useState(null)
const [selectedVoice, setSelectedVoice] = useState(null) const [selectedVoice, setSelectedVoice] = useState(null)
// 已移除字幕、透明背景和背景图片 URL 相关配置
const [textValue, setTextValue] = useState('') const [textValue, setTextValue] = useState('')
const [playingVoiceId, setPlayingVoiceId] = useState(null) const [playingVoiceId, setPlayingVoiceId] = useState(null)
const [videoSize, setVideoSize] = useState(
getConfiguredDigitalHumanType() === DIGITAL_HUMAN_TYPES.ADVANCED
? ADVANCED_TEMPLATES[0].ratio
: 'landscape',
)
const [isInserting, setIsInserting] = useState(false)
const [activeMode, setActiveMode] = useState(getConfiguredDigitalHumanType)
const type =
activeMode === DIGITAL_HUMAN_TYPES.ADVANCED
? DIGITAL_HUMAN_TYPES.ADVANCED
: DIGITAL_HUMAN_TYPES.BASIC
const [language, setLanguage] = useState('auto')
const [speed, setSpeed] = useState(5)
const [volume, setVolume] = useState(5)
const [pitch, setPitch] = useState(5)
const [figurePickerOpen, setFigurePickerOpen] = useState(false)
const [figureFilters, setFigureFilters] = useState({
gender: 'all',
style: 'all',
pose: 'all',
background: 'all',
})
const [voicePickerOpen, setVoicePickerOpen] = useState(false)
const [voiceGender, setVoiceGender] = useState('female')
const [templateId, setTemplateId] = useState(ADVANCED_TEMPLATES[0].value)
const [model, setModel] = useState(DEFAULT_MODEL)
const [title, setTitle] = useState('')
const [logoUrl, setLogoUrl] = useState('')
const [bgmUrl, setBgmUrl] = useState('')
const [backgroundImageUrl, setBackgroundImageUrl] = useState('')
const [materialUrl, setMaterialUrl] = useState('')
const [extendBackgroundUrl, setExtendBackgroundUrl] = useState('')
const [riskTip, setRiskTip] = useState('')
const [openingMaterial, setOpeningMaterial] = useState(null)
const [endingMaterial, setEndingMaterial] = useState(null)
const [mashupMaterials, setMashupMaterials] = useState([])
const [fissionFigureIds, setFissionFigureIds] = useState([])
const [fissionVoiceIds, setFissionVoiceIds] = useState([])
const audioRef = useRef(null)
const previewDragRef = useRef(null)
const [previewImageAspect, setPreviewImageAspect] = useState(null)
const [showGeneratedPreview, setShowGeneratedPreview] = useState(false)
const [previewFigureSelected, setPreviewFigureSelected] = useState(false)
const [selectedHistoryTaskId, setSelectedHistoryTaskId] = useState(null)
const { const {
figures, figures,
...@@ -52,51 +168,422 @@ export default function AIDigitalHumanModal(props) { ...@@ -52,51 +168,422 @@ export default function AIDigitalHumanModal(props) {
loadingFigures, loadingFigures,
isGenerating, isGenerating,
generatedVideo, generatedVideo,
error,
tasks,
activeTask,
generateVideo, generateVideo,
selectTask,
resumeTask,
retryTask,
refreshFigures,
} = useDigitalHuman() } = useDigitalHuman()
// 初始化默认选择 const maxTextLength = type === DIGITAL_HUMAN_TYPES.ADVANCED ? 2000 : 20000
useEffect(() => { const requiredItems = [
if (!selectedFigure && figures.length > 0) { { label: '选择 2D 小样本数字人', done: !!selectedFigure },
setSelectedFigure(figures[0]) ...(type === DIGITAL_HUMAN_TYPES.ADVANCED
? [{ label: '选择视频样式', done: !!templateId }]
: []),
{ label: '填写讲课内容', done: !!textValue.trim() },
{ label: '确认配音', done: !!selectedVoice },
]
const nextRequiredItem = requiredItems.find((item) => !item.done)?.label
const canGenerate = requiredItems.every((item) => item.done)
const selectedTemplate = ADVANCED_TEMPLATES.find((item) => item.value === templateId)
const allVoices = useMemo(() => [...femaleVoices, ...maleVoices], [femaleVoices, maleVoices])
const figureVideoSize = getFigureVideoSize(selectedFigure)
const outputVideoSize = videoSize
const figureFilterOptions = useMemo(() => {
const styles = new Set()
const poses = new Set()
const backgrounds = new Set()
figures.forEach((figure) => {
const tags = figure.tags || []
tags.forEach((tag) => {
if (!['视频', '2D'].includes(tag)) styles.add(tag)
})
if (figure.pose) poses.add(figure.pose)
if (figure.background) backgrounds.add(figure.background)
})
return {
styles: [...styles],
poses: [...poses],
backgrounds: [...backgrounds],
} }
if (!selectedVoice && femaleVoices.length > 0) { }, [figures])
setSelectedVoice(femaleVoices[0]) const filteredFigures = useMemo(
() =>
figures.filter((figure) => {
const gender = String(figure.gender || '').toLowerCase()
const matchesGender =
figureFilters.gender === 'all' ||
gender === figureFilters.gender ||
(figureFilters.gender === 'female' && gender === 'f') ||
(figureFilters.gender === 'male' && gender === 'm')
const matchesStyle =
figureFilters.style === 'all' || (figure.tags || []).includes(figureFilters.style)
const matchesPose = figureFilters.pose === 'all' || figure.pose === figureFilters.pose
const matchesBackground =
figureFilters.background === 'all' || figure.background === figureFilters.background
return matchesGender && matchesStyle && matchesPose && matchesBackground
}),
[figureFilters, figures],
)
const visibleTasks = useMemo(() => tasks.filter((task) => task.type === type), [tasks, type])
const activeVideoTask = activeTask?.type === type ? activeTask : null
const previewLayoutDefaults = (size = videoSize) =>
size === 'portrait' ? { x: 50, y: 50, scale: 1 } : { x: 50, y: 50, scale: 1 }
const [previewLayout, setPreviewLayout] = useState(() => previewLayoutDefaults())
const previewSize = videoSizes[outputVideoSize]
const previewFigureSize = getFigureLayerSize(previewSize, selectedFigure, previewImageAspect)
const previewDhParams = buildPreviewDhParams(
previewSize,
previewLayout,
selectedFigure,
previewImageAspect,
)
const previewLayerStyle = {
width: `${(previewFigureSize.width / previewSize.width) * 100}%`,
height: `${(previewFigureSize.height / previewSize.height) * 100}%`,
left: `${previewLayout.x}%`,
top: `${previewLayout.y}%`,
transform: `translate(-50%, -50%) scale(${previewLayout.scale})`,
}
const generatedPreviewMatches =
!!generatedVideo &&
(selectedHistoryTaskId === generatedVideo.taskId ||
!generatedVideo.options ||
(generatedVideo.options.type === type &&
(type === DIGITAL_HUMAN_TYPES.ADVANCED && fissionFigureIds.length
? fissionFigureIds.map(String).includes(String(generatedVideo.options.figureId))
: String(generatedVideo.options.figureId) === String(selectedFigure?.id)) &&
generatedVideo.options.driveType === 'TEXT' &&
generatedVideo.options.text === textValue.trim() &&
(type === DIGITAL_HUMAN_TYPES.ADVANCED && fissionVoiceIds.length
? fissionVoiceIds.map(String).includes(String(generatedVideo.options.voiceId))
: String(generatedVideo.options.voiceId) === String(selectedVoice?.id)) &&
Number(generatedVideo.options.speed) === speed &&
Number(generatedVideo.options.volume) === volume &&
Number(generatedVideo.options.pitch) === pitch &&
!generatedVideo.options.transparent &&
Number(generatedVideo.options.width) === previewSize.width &&
Number(generatedVideo.options.height) === previewSize.height &&
(type === DIGITAL_HUMAN_TYPES.ADVANCED
? (generatedVideo.options.model || DEFAULT_MODEL) === model &&
generatedVideo.options.templateId === templateId &&
(generatedVideo.options.title || '') === title &&
(generatedVideo.options.logoUrl || '') === logoUrl &&
(generatedVideo.options.bgmUrl || '') === bgmUrl &&
(generatedVideo.options.materialUrl || '') === materialUrl &&
(generatedVideo.options.extendBackgroundUrl || '') === extendBackgroundUrl &&
(generatedVideo.options.riskTip || '') === riskTip &&
(generatedVideo.options.openingFileId || '') === (openingMaterial?.fileId || '') &&
(generatedVideo.options.endingFileId || '') === (endingMaterial?.fileId || '') &&
JSON.stringify(generatedVideo.options.mashupMaterials || []) ===
JSON.stringify(mashupMaterials) &&
JSON.stringify(generatedVideo.options.fissionFigureIds || []) ===
JSON.stringify(fissionFigureIds) &&
JSON.stringify(generatedVideo.options.fissionVoiceIds || []) ===
JSON.stringify(fissionVoiceIds)
: (generatedVideo.options.language || 'auto') === language &&
(generatedVideo.options.backgroundImageUrl || '') === backgroundImageUrl &&
JSON.stringify(generatedVideo.options.dhParams) === JSON.stringify(previewDhParams))))
const generatedVideoWidth = Number(generatedVideo?.options?.width)
const generatedVideoHeight = Number(generatedVideo?.options?.height)
const generatedVideoSize =
generatedVideoWidth && generatedVideoHeight
? generatedVideoWidth >= generatedVideoHeight
? 'landscape'
: 'portrait'
: outputVideoSize
const previewStageSize =
showGeneratedPreview && generatedPreviewMatches && generatedVideo?.options
? generatedVideoSize
: outputVideoSize
useEffect(() => {
if (!selectedFigure && figures.length > 0) setSelectedFigure(figures[0])
if (selectedFigure) {
const latestFigure = figures.find((figure) => String(figure.id) === String(selectedFigure.id))
if (latestFigure && latestFigure !== selectedFigure) setSelectedFigure(latestFigure)
} }
if (!selectedVoice && femaleVoices.length > 0) setSelectedVoice(femaleVoices[0])
}, [figures, femaleVoices, selectedFigure, selectedVoice]) }, [figures, femaleVoices, selectedFigure, selectedVoice])
useEffect(() => {
if (type === DIGITAL_HUMAN_TYPES.BASIC && figureVideoSize) setVideoSize(figureVideoSize)
}, [figureVideoSize, selectedFigure?.id, type])
useEffect(() => {
setPreviewLayout(previewLayoutDefaults(outputVideoSize))
}, [selectedFigure?.id, outputVideoSize])
useEffect(() => {
setPreviewImageAspect(null)
}, [selectedFigure?.id])
useEffect(() => {
setShowGeneratedPreview(!!generatedVideo)
}, [generatedVideo])
useEffect(() => {
setSelectedHistoryTaskId(null)
}, [
type,
selectedFigure?.id,
selectedVoice?.id,
textValue,
language,
speed,
volume,
pitch,
videoSize,
templateId,
model,
title,
logoUrl,
bgmUrl,
backgroundImageUrl,
materialUrl,
extendBackgroundUrl,
riskTip,
openingMaterial,
endingMaterial,
mashupMaterials,
fissionFigureIds,
fissionVoiceIds,
previewLayout,
])
const handleVoicePlay = (voice) => { const handleVoicePlay = (voice) => {
if (!voice || !voice.previewUrl || !audioRef.current) return
const audio = audioRef.current
if (playingVoiceId === voice.id) { if (playingVoiceId === voice.id) {
audioRef.current.pause() audio.pause()
setPlayingVoiceId(null)
return
}
audio.src = voice.previewUrl
audio.play().catch(() => {
setPlayingVoiceId(null) setPlayingVoiceId(null)
} else { message.error('音色试听失败')
audioRef.current.src = voice.previewUrl })
audioRef.current.play()
setPlayingVoiceId(voice.id) setPlayingVoiceId(voice.id)
audioRef.current.onended = () => setPlayingVoiceId(null) }
const handleVoiceSelect = (voice) => {
setSelectedVoice(voice)
setVoicePickerOpen(false)
}
const renderVoiceGrid = (voices) => {
return (
<>
<div className='voice-grid'>
{voices.map((voice) => (
<div
role='button'
tabIndex={0}
key={voice.id}
className={`voice-item ${selectedVoice?.id === voice.id ? 'active' : ''}`}
onClick={() => handleVoiceSelect(voice)}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
handleVoiceSelect(voice)
}
}}
>
<span className='voice-picker-avatar'>
<UserOutlined />
</span>
<span className='v-info'>
<strong>{voice.name}</strong>
<small>{voice.style}</small>
</span>
<Button
type='text'
shape='circle'
disabled={!voice.previewUrl}
aria-label={voice.previewUrl ? `试听${voice.name}` : `${voice.name}暂无试听`}
icon={
playingVoiceId === voice.id ? <PauseCircleOutlined /> : <PlayCircleOutlined />
}
onClick={(event) => {
event.stopPropagation()
handleVoicePlay(voice)
}}
/>
</div>
))}
</div>
</>
)
}
const renderVoiceSlider = (label, value, onChange, low, high) => (
<div className='range-setting'>
<div className='range-label'>{label}</div>
<div className='range-control'>
<span className='range-edge'>{low}</span>
<Slider
min={0}
max={type === DIGITAL_HUMAN_TYPES.ADVANCED ? 15 : 10}
value={value}
onChange={onChange}
tooltip={{ open: false }}
/>
<span className='range-edge'>{high}</span>
<InputNumber
min={0}
max={type === DIGITAL_HUMAN_TYPES.ADVANCED ? 15 : 10}
value={value}
onChange={(next) => onChange(next ?? 0)}
/>
</div>
</div>
)
const handlePreviewPointerDown = (event, mode = 'move') => {
setPreviewFigureSelected(true)
const figure = event.currentTarget.closest('.preview-layer') || event.currentTarget
const figureRect = figure.getBoundingClientRect()
figure.setPointerCapture(event.pointerId)
previewDragRef.current = {
pointerId: event.pointerId,
mode,
startX: event.clientX,
startY: event.clientY,
layout: previewLayout,
layerWidthPx: figureRect.width,
layerHeightPx: figureRect.height,
}
}
const handlePreviewPointerMove = (event) => {
const drag = previewDragRef.current
if (!drag || drag.pointerId !== event.pointerId) return
const stage = event.currentTarget.closest('.preview-stage')
if (!stage) return
const rect = stage.getBoundingClientRect()
if (drag.mode !== 'move') {
// 拖动对角时锚定对角的另一条边:宽度/高度变化的一半平移到中心点
const horizontalDirection = drag.mode.endsWith('e') ? 1 : -1
const verticalDirection = drag.mode.startsWith('s') ? 1 : -1
const widthDelta = (event.clientX - drag.startX) * horizontalDirection
const heightDelta = (event.clientY - drag.startY) * verticalDirection
const widthScaleDelta = widthDelta / Math.max(1, drag.layerWidthPx)
const heightScaleDelta = heightDelta / Math.max(1, drag.layerHeightPx)
const scaleDelta =
Math.abs(widthScaleDelta) >= Math.abs(heightScaleDelta) ? widthScaleDelta : heightScaleDelta
const scale = Math.max(0.3, Math.min(1.5, drag.layout.scale * (1 + scaleDelta)))
const scaleRatio = scale / drag.layout.scale
const resizedWidthDelta = drag.layerWidthPx * (scaleRatio - 1)
const resizedHeightDelta = drag.layerHeightPx * (scaleRatio - 1)
setPreviewLayout({
...drag.layout,
scale,
x: drag.layout.x + ((horizontalDirection * resizedWidthDelta) / 2 / rect.width) * 100,
y: drag.layout.y + ((verticalDirection * resizedHeightDelta) / 2 / rect.height) * 100,
})
return
}
setPreviewLayout({
...drag.layout,
x: drag.layout.x + ((event.clientX - drag.startX) / rect.width) * 100,
y: drag.layout.y + ((event.clientY - drag.startY) / rect.height) * 100,
})
}
const handlePreviewPointerUp = (event) => {
if (previewDragRef.current?.pointerId === event.pointerId) {
previewDragRef.current = null
}
}
const handleResetPreview = () => {
setPreviewLayout(previewLayoutDefaults(outputVideoSize))
setShowGeneratedPreview(false)
setPreviewFigureSelected(false)
setSelectedHistoryTaskId(null)
}
const handleWorkspaceChange = (nextMode) => {
setActiveMode(nextMode)
setShowGeneratedPreview(false)
setPreviewFigureSelected(false)
setSelectedHistoryTaskId(null)
if (nextMode === DIGITAL_HUMAN_TYPES.ADVANCED) {
setVideoSize(selectedTemplate?.ratio || 'portrait')
} else if (nextMode === DIGITAL_HUMAN_TYPES.BASIC) {
setVideoSize(figureVideoSize || 'landscape')
setSpeed((current) => Math.min(current, 10))
setVolume((current) => Math.min(current, 10))
setPitch((current) => Math.min(current, 10))
} }
} }
const handleGenerate = async () => { const handleGenerate = async () => {
if (!canGenerate) return message.warning(`请先完成:${nextRequiredItem}`)
if (!selectedFigure) return message.warning('请选择数字人形象') if (!selectedFigure) return message.warning('请选择数字人形象')
if (!textValue.trim()) return message.warning('请输入播报文本') if (!textValue.trim()) return message.warning('请输入播报文本')
if (!selectedVoice) return message.warning('请选择音色') if (!selectedVoice) return message.warning('请选择音色')
try { try {
setSelectedHistoryTaskId(null)
const size = videoSizes[outputVideoSize]
await generateVideo({ await generateVideo({
type,
figureId: selectedFigure.id, figureId: selectedFigure.id,
figureName: selectedFigure.name, figureName: selectedFigure.name,
driveType: 'TEXT', driveType: 'TEXT',
text: textValue.trim(), text: textValue.trim(),
voiceId: selectedVoice?.id, voiceId: selectedVoice?.id,
width: 720, speed,
height: 1280, volume,
pitch,
language:
type === DIGITAL_HUMAN_TYPES.ADVANCED || language === 'auto' ? undefined : language,
width: size.width,
height: size.height,
transparent: false,
// 预览拖拽出的位置只对基础接口生效,模板样式由模板自身控制布局
dhParams: type === DIGITAL_HUMAN_TYPES.BASIC ? previewDhParams : undefined,
enableSubtitle: false,
model,
templateId,
title,
logoUrl,
bgmUrl,
backgroundImageUrl,
materialUrl,
extendBackgroundUrl,
riskTip,
openingFileId: openingMaterial?.fileId,
endingFileId: endingMaterial?.fileId,
mashupMaterials,
fissionFigureIds,
fissionVoiceIds,
}) })
} catch (error) {} } catch {}
}
const handleRetry = async () => {
try {
await retryTask()
} catch (retryError) {
message.error(retryError.message || '重新提交失败')
}
} }
const handleInsert = async () => { const handleInsert = async () => {
if (!generatedVideo) return if (!generatedVideo) return
setIsInserting(true)
try { try {
message.loading({ content: '处理中...', key: 'inserting' }) message.loading({ content: '处理中...', key: 'inserting' })
const ossUrl = await uploadFileByUrl(generatedVideo.url) const ossUrl = await uploadFileByUrl(generatedVideo.url)
...@@ -112,219 +599,670 @@ export default function AIDigitalHumanModal(props) { ...@@ -112,219 +599,670 @@ export default function AIDigitalHumanModal(props) {
message.success({ content: '已插入文章', key: 'inserting' }) message.success({ content: '已插入文章', key: 'inserting' })
setIsModalOpen(false) setIsModalOpen(false)
} }
} catch (error) { } catch {
message.error({ content: '插入失败', key: 'inserting' }) message.error({ content: '插入失败', key: 'inserting' })
} finally {
setIsInserting(false)
} }
} }
const handleClose = () => {
audioRef.current?.pause()
setIsModalOpen(false)
}
return ( return (
<Modal <Modal
title='数字人 AI 视频生成' title={
<div className='digital-human-modal-title'>
<span>数字人</span>
<small>视频合成与形象定制</small>
</div>
}
open={isModalOpen} open={isModalOpen}
onCancel={() => setIsModalOpen(false)} onCancel={handleClose}
footer={null} footer={null}
width={1000} width={1600}
className='digital-human-modal'
centered centered
destroyOnHidden destroyOnHidden
> >
<audio ref={audioRef} onEnded={() => setPlayingVoiceId(null)} />
<div className='ai-digital-human'> <div className='ai-digital-human'>
<Row gutter={[24, 16]}> <Tabs
<Col span={24}> className='digital-human-workspace-tabs'
{/* 形象选择 */} activeKey={activeMode}
<div className='section-box'> items={workspaceTabs}
<div className='section-title'>1. 选择数字人形象</div> onChange={handleWorkspaceChange}
{loadingFigures && figures.length === 0 ? (
<div style={{ textAlign: 'center', padding: '20px' }}>
<Spin tip='同步云端形象中...' />
</div>
) : figures.length > 0 ? (
<div className='figure-list'>
{figures.map((fig) => (
<div
key={fig.id}
className={`figure-item ${selectedFigure?.id === fig.id ? 'active' : ''}`}
onClick={() => setSelectedFigure(fig)}
>
<div className='avatar-wrapper'>
{fig.avatar ? (
<img
src={fig.avatar}
className='figure-avatar'
onError={(e) => {
e.target.style.display = 'none'
e.target.nextSibling.style.display = 'flex'
}}
/> />
{activeMode === CUSTOMIZE_WORKSPACE ? (
<DigitalHumanCustomizePanel onSuccess={refreshFigures} />
) : null} ) : null}
<div <div
className='avatar-fallback' className={`component-layout ${activeMode === CUSTOMIZE_WORKSPACE ? 'is-hidden' : ''}`}
style={{ display: fig.avatar ? 'none' : 'flex' }}
> >
<UserOutlined /> <div className='config-column'>
</div> <section className='config-section figure-section'>
</div> <h2>形象</h2>
<div className='figure-name'>{fig.name}</div> <button
</div> type='button'
))} className='selected-media-card'
</div> onClick={() => setFigurePickerOpen(true)}
disabled={loadingFigures && figures.length === 0}
>
<span className='selected-media-thumb'>
{selectedFigure?.avatar ? (
<img src={selectedFigure.avatar} alt={selectedFigure.name} />
) : ( ) : (
<Empty description='暂无可用形象' /> <UserOutlined />
)} )}
</div> </span>
<span className='selected-media-copy'>
<strong>{selectedFigure?.name || '请选择形象'}</strong>
<span>
ID:{selectedFigure?.id || '--'} <CopyOutlined />
</span>
</span>
<SwapOutlined className='selected-media-action' />
</button>
</section>
{/* 驱动配置 */} <Collapse
<div className='section-box'> ghost
<div className='section-title'>2. 配置播报内容与音色</div> expandIconPosition='end'
<div className='drive-content'> defaultActiveKey={['voice']}
className='config-collapse'
items={[
{
key: 'voice',
label: '声音输出设置',
children: (
<div className='voice-output-settings'>
<div className='field-label'>声音</div>
<button
type='button'
className='selected-media-card voice-card'
onClick={() => setVoicePickerOpen(true)}
>
<span className='selected-media-thumb voice-avatar'>
<UserOutlined />
</span>
<span className='selected-media-copy'>
<strong>{selectedVoice?.name || '请选择声音'}</strong>
<span>
ID:{selectedVoice?.id || '--'} <CopyOutlined />
</span>
<span className='voice-tags'>
<em>{selectedVoice?.gender || '女声'}</em>
<em>中文、方言</em>
{selectedVoice?.style ? <em>{selectedVoice.style}</em> : null}
</span>
</span>
<SwapOutlined className='selected-media-action' />
</button>
<div className='field-label'>语言</div>
<Select
className='full-control'
value={type === DIGITAL_HUMAN_TYPES.ADVANCED ? 'auto' : language}
onChange={setLanguage}
disabled={type === DIGITAL_HUMAN_TYPES.ADVANCED}
options={[
{ label: '自动', value: 'auto' },
{ label: '中文', value: 'Chinese' },
{ label: '方言', value: 'Chinese,Yue' },
]}
/>
{type === DIGITAL_HUMAN_TYPES.ADVANCED ? (
<div className='field-hint'>高级视频模板暂不支持多语言。</div>
) : null}
{renderVoiceSlider('语速', speed, setSpeed, '慢', '快')}
{renderVoiceSlider('音量', volume, setVolume, '小', '大')}
{renderVoiceSlider('语调', pitch, setPitch, '低', '高')}
<section className='script-section'>
<div className='field-label'>播报文案</div>
<div className='script-box'>
<TextArea <TextArea
placeholder='请输入要播报的文本内容...' bordered={false}
rows={4} placeholder='请输入数字人要播报的内容'
maxLength={2000} rows={7}
maxLength={maxTextLength}
showCount showCount
value={textValue} value={textValue}
onChange={(e) => setTextValue(e.target.value)} onChange={(event) => setTextValue(event.target.value)}
style={{ marginBottom: 12 }}
/> />
<div className='voice-selection'> </div>
<div className='sub-title'>精选配音:</div> </section>
<Tabs </div>
size='small' ),
},
]}
/>
<Collapse
ghost
expandIconPosition='end'
defaultActiveKey={['base']}
className='config-collapse output-collapse'
items={[ items={[
{ {
key: 'female', key: 'base',
label: '女声', label: '基础输出设置',
children: ( children: (
<div className='voice-grid'> <div className='output-settings'>
{femaleVoices.map((v) => ( <div className='field-label'>输出画面</div>
<div <div className='choice-grid'>
key={v.id} {['landscape', 'portrait'].map((size) => (
className={`voice-item ${selectedVoice?.id === v.id ? 'active' : ''}`} <button
onClick={() => setSelectedVoice(v)} type='button'
key={size}
className={outputVideoSize === size ? 'active' : ''}
onClick={() => setVideoSize(size)}
> >
<div className='v-info'> <span className={`ratio-icon ${size}`} />
<span className='v-name'>{v.name}</span> <span>{size === 'landscape' ? '16:9' : '9:16'}</span>
<span className='v-style'>{v.style}</span> <small>
</div> {videoSizes[size].width}*{videoSizes[size].height}
<Button </small>
type='text' </button>
shape='circle'
icon={
playingVoiceId === v.id ? (
<PauseCircleOutlined />
) : (
<PlayCircleOutlined />
)
}
onClick={(e) => {
e.stopPropagation()
handleVoicePlay(v)
}}
/>
</div>
))} ))}
</div> </div>
</div>
), ),
}, },
{ {
key: 'male', key: 'advanced',
label: '男声', label: '高级输出设置',
children: ( children: (
<div className='voice-grid'> <div className='output-settings'>
{maleVoices.map((v) => ( {type === DIGITAL_HUMAN_TYPES.ADVANCED ? (
<div <>
key={v.id} <div className='field-label'>视频样式</div>
className={`voice-item ${selectedVoice?.id === v.id ? 'active' : ''}`} <div className='template-grid'>
onClick={() => setSelectedVoice(v)} {ADVANCED_TEMPLATES.map((template) => (
<button
type='button'
key={template.value}
className={`template-card ${templateId === template.value ? 'active' : ''}`}
onClick={() => {
setTemplateId(template.value)
setVideoSize(template.ratio)
}}
> >
<div className='v-info'> <span className={`template-preview ${template.ratio}`}>
<span className='v-name'>{v.name}</span> <img src={template.preview} alt={`${template.label}效果预览`} />
<span className='v-style'>{v.style}</span> </span>
<span className='template-meta'>
<strong>{template.label}</strong>
<small>{template.ratio === 'landscape' ? '横屏' : '竖屏'}</small>
</span>
{templateId === template.value ? (
<CheckOutlined className='template-selected' />
) : null}
</button>
))}
</div> </div>
<Button <div className='advanced-field'>
type='text' <div className='field-label'>生成模型</div>
shape='circle' <Select
icon={ className='full-control'
playingVoiceId === v.id ? ( value={model}
<PauseCircleOutlined /> onChange={setModel}
) : ( options={[
<PlayCircleOutlined /> { label: '高质量(quality_v2)', value: 'quality_v2' },
) { label: '快速(turbo_v2)', value: 'turbo_v2' },
} ]}
onClick={(e) => { />
e.stopPropagation() </div>
handleVoicePlay(v) <div className='advanced-field'>
<div className='field-label'>视频标题</div>
<Input
maxLength={30}
showCount
value={title}
onChange={(event) => setTitle(event.target.value)}
placeholder='可选,最多 30 个字符'
/>
</div>
<DigitalHumanUploadField
label='Logo(可选)'
value={logoUrl}
onChange={setLogoUrl}
accept='.png,.jpg,.jpeg,.bmp'
maxSizeMB={3}
filenamePrefix='digital-human-logo'
previewType='image'
uploadHint='PNG、JPG、JPEG、BMP,不超过 3MB'
/>
<DigitalHumanUploadField
label='背景音乐(可选)'
value={bgmUrl}
onChange={setBgmUrl}
accept='.wav,.mp3,.wma,.m4a'
maxSizeMB={120}
filenamePrefix='digital-human-bgm'
hint='支持 WAV、MP3、WMA、M4A,不超过 120MB。'
/>
<DigitalHumanUploadField
label='模板背景素材(可选)'
value={materialUrl}
onChange={setMaterialUrl}
accept='.png,.jpg,.jpeg,.bmp,.mp4'
maxImageSizeMB={3}
maxVideoSizeMB={500}
filenamePrefix='digital-human-material'
hint='图片不超过 3MB,MP4 视频不超过 500MB。'
/>
<DigitalHumanUploadField
label='扩展自定义背景(可选)'
value={extendBackgroundUrl}
onChange={setExtendBackgroundUrl}
accept='.png,.jpg,.jpeg,.bmp,.mp4'
maxImageSizeMB={3}
maxVideoSizeMB={500}
filenamePrefix='digital-human-extend-background'
hint='横屏输出使用横屏扩展背景、竖屏输出使用竖屏扩展背景;模板和模板背景素材比例与输出相反。'
/>
<div className='advanced-field'>
<div className='field-label'>水印描述(可选)</div>
<Input
maxLength={30}
showCount
value={riskTip}
onChange={(event) => setRiskTip(event.target.value)}
placeholder='默认显示在视频底部居中'
/>
</div>
<DigitalHumanBaiduUploadField
label='片头视频'
value={openingMaterial}
onChange={setOpeningMaterial}
accept='.mp4,video/mp4'
hint='可选,仅支持 MP4,不超过 500MB。'
/>
<DigitalHumanBaiduUploadField
label='片尾视频'
value={endingMaterial}
onChange={setEndingMaterial}
accept='.mp4,video/mp4'
hint='可选,仅支持 MP4,不超过 500MB。'
/>
<DigitalHumanBaiduUploadField
label='混剪素材'
value={mashupMaterials}
onChange={setMashupMaterials}
accept='.png,.jpg,.jpeg,.bmp,.mp4'
multiple
maxCount={10}
hint='可选,最多 10 个;图片不超过 3MB,MP4 视频不超过 500MB。'
/>
<div className='advanced-field'>
<div className='field-label'>裂变形象(可选)</div>
<Select
className='full-control'
mode='multiple'
maxCount={10}
allowClear
value={fissionFigureIds}
onChange={(values) => {
setFissionFigureIds(values)
if (!values.length) setFissionVoiceIds([])
}} }}
placeholder='最多选择 10 个形象'
options={figures.map((figure) => ({
label: `${figure.name}(${figure.id})`,
value: String(figure.id),
}))}
/> />
<div className='field-hint'>选择后会覆盖当前形象并批量生成视频。</div>
</div> </div>
))} <div className='advanced-field'>
<div className='field-label'>裂变音色(可选)</div>
<Select
className='full-control'
mode='multiple'
maxCount={10}
allowClear
disabled={!fissionFigureIds.length}
value={fissionVoiceIds}
onChange={setFissionVoiceIds}
placeholder='先选择裂变形象,最多选择 10 个音色'
options={allVoices.map((voice) => ({
label: `${voice.name}(${voice.id})`,
value: String(voice.id),
}))}
/>
<div className='field-hint'>不选择时,仅按形象裂变。</div>
</div>
</>
) : null}
{type === DIGITAL_HUMAN_TYPES.BASIC ? (
<DigitalHumanUploadField
label='背景图片(可选)'
value={backgroundImageUrl}
onChange={setBackgroundImageUrl}
accept='image/*'
maxSizeMB={3}
filenamePrefix='digital-human-background'
previewType='image'
hint='不上传也可以直接生成视频。'
/>
) : null}
</div> </div>
), ),
}, },
]} ]}
/> />
</div> </div>
</div>
{/* 已彻底移除背景图片 URL 相关输入 */} <section className='preview-panel'>
<div className='preview-panel-heading'>
<div>
<strong>展示调整</strong>
<span>支持拖动调整数字人位置和大小</span>
</div> </div>
</Col> <Button type='text' icon={<ReloadOutlined />} onClick={handleResetPreview}>
还原
<Col span={24}> </Button>
<div className='preview-panel'> </div>
<div className='section-title'>生成状态</div> <section className='generation-result'>
<div className='results-container'> {activeVideoTask?.status ? (
<div className='generation-result-heading'>
<strong>生成状态</strong>
<Tag color={taskStatus[activeVideoTask.status]?.color}>
{taskStatus[activeVideoTask.status]?.text || activeVideoTask.status}
</Tag>
</div>
) : null}
{activeVideoTask && error ? (
<Alert
type='warning'
showIcon
message={error}
action={
activeVideoTask.status === 'PAUSED' ? (
<Button size='small' icon={<ReloadOutlined />} onClick={() => resumeTask()}>
继续查询
</Button>
) : activeVideoTask.status === 'FAILED' ? (
<Button size='small' icon={<ReloadOutlined />} onClick={handleRetry}>
重新生成
</Button>
) : null
}
/>
) : null}
<div
className={`preview-stage ${previewStageSize}`}
onPointerDown={(event) => {
if (event.target === event.currentTarget) setPreviewFigureSelected(false)
}}
style={{
backgroundImage:
type === DIGITAL_HUMAN_TYPES.BASIC && backgroundImageUrl
? `url(${backgroundImageUrl})`
: type === DIGITAL_HUMAN_TYPES.ADVANCED && selectedTemplate?.preview
? `url(${selectedTemplate.preview})`
: undefined,
}}
>
{showGeneratedPreview && generatedPreviewMatches ? (
<div className='generated-preview-clip'>
<video
className='generated-preview-video'
src={generatedVideo.url}
controls
aria-label='生成的数字人视频'
/>
</div>
) : (
<>
<div className='preview-canvas-clip'>
<div className='preview-image-layer' style={previewLayerStyle}>
{selectedFigure?.avatar ? (
<img
src={selectedFigure.avatar}
alt={selectedFigure.name}
onLoad={(event) => {
const { naturalWidth, naturalHeight } = event.currentTarget
if (naturalWidth && naturalHeight) {
setPreviewImageAspect(naturalHeight / naturalWidth)
}
}}
/>
) : (
<UserOutlined />
)}
</div>
</div>
<div
className={`preview-layer ${previewFigureSelected ? 'selected' : ''}`}
style={previewLayerStyle}
onPointerDown={handlePreviewPointerDown}
onPointerMove={handlePreviewPointerMove}
onPointerUp={handlePreviewPointerUp}
onPointerCancel={handlePreviewPointerUp}
role='img'
aria-selected={previewFigureSelected}
aria-label='可拖动调整数字人视频位置和大小的预览'
>
{previewFigureSelected
? ['nw', 'ne', 'sw', 'se'].map((corner) => (
<span
key={corner}
className={`preview-resize-handle ${corner}`}
onPointerDown={(event) => {
event.stopPropagation()
handlePreviewPointerDown(event, corner)
}}
/>
))
: null}
</div>
</>
)}
{isGenerating ? ( {isGenerating ? (
<div className='generating-box'> <div className='generating-box'>
<Spin size='large' /> <Spin />
<div className='gen-text'>视频正在录制中...</div> <span>视频正在生成</span>
<div className='gen-hint'>预计 1 分钟左右完成</div> </div>
) : null}
</div> </div>
) : generatedVideo ? ( {generatedVideo && generatedPreviewMatches ? (
<div className='final-video-box'>
<video src={generatedVideo.url} controls autoPlay />
<div className='video-actions'> <div className='video-actions'>
<Button <Button
block
size='large'
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
onClick={() => saveAs(generatedVideo.url, 'ai_video.mp4')} onClick={() => saveAs(generatedVideo.url, 'ai_video.mp4')}
> >
下载视频 下载视频
</Button> </Button>
{generatedVideo.subtitleFileUrl ? (
<Button
icon={<DownloadOutlined />}
onClick={() => saveAs(generatedVideo.subtitleFileUrl, 'subtitle.srt')}
>
下载字幕
</Button>
) : null}
<Button <Button
block
type='primary' type='primary'
size='large'
icon={<CheckOutlined />} icon={<CheckOutlined />}
onClick={handleInsert} onClick={handleInsert}
loading={isInserting}
> >
插入编辑器 插入文章
</Button> </Button>
</div> </div>
</div> ) : null}
) : (
<div className='empty-preview'>
<VideoCameraOutlined style={{ fontSize: 64, opacity: 0.1 }} />
<p>等待配置完成后点击开始</p>
</div>
)}
</div>
<Divider style={{ margin: '12px 0' }} />
<Button <Button
type='primary' type='primary'
size='large'
block block
icon={<VideoCameraOutlined />} className='generation-submit'
onClick={handleGenerate} onClick={handleGenerate}
loading={isGenerating} loading={isGenerating}
className='submit-btn'
> >
开始生成 AI 视频 生成数字人视频
</Button> </Button>
</section>
</section>
</div>
{activeMode !== CUSTOMIZE_WORKSPACE && visibleTasks.length > 0 ? (
<div className='section-box task-history'>
<div className='section-title'>最近生成</div>
<div className='task-list'>
{visibleTasks.map((task) => (
<button
type='button'
key={task.taskId}
className={`task-item ${activeTask?.taskId === task.taskId ? 'active' : ''}`}
onClick={() => {
setSelectedHistoryTaskId(
task.status === 'SUCCESS' && task.url ? task.taskId : null,
)
selectTask(task)
}}
>
<span className='task-name'>
{DIGITAL_HUMAN_TYPE_LABELS[task.type] || '基础接口'} ·{' '}
{task.figureName || '视频'}
</span>
<span className='task-time'>{new Date(task.createdAt).toLocaleString()}</span>
<Tag color={taskStatus[task.status]?.color}>
{taskStatus[task.status]?.text || task.status}
</Tag>
</button>
))}
</div>
</div> </div>
</Col> ) : null}
</Row>
</div> </div>
<Modal
open={figurePickerOpen}
title='选择形象'
footer={null}
onCancel={() => setFigurePickerOpen(false)}
width={920}
className='digital-human-picker-modal figure-picker-modal'
>
{loadingFigures && figures.length === 0 ? (
<div className='picker-loading'>
<Spin tip='同步云端形象中...' />
</div>
) : figures.length > 0 ? (
<>
<Space wrap size='middle'>
<Select
value={figureFilters.gender}
popupMatchSelectWidth={false}
options={[
{ label: '全部性别', value: 'all' },
{ label: '女性', value: 'female' },
{ label: '男性', value: 'male' },
]}
onChange={(value) => setFigureFilters((current) => ({ ...current, gender: value }))}
/>
<Select
value={figureFilters.style}
popupMatchSelectWidth={false}
options={[
{ label: '全部风格', value: 'all' },
...figureFilterOptions.styles.map((style) => ({ label: style, value: style })),
]}
onChange={(value) => setFigureFilters((current) => ({ ...current, style: value }))}
/>
<Select
value={figureFilters.pose}
popupMatchSelectWidth={false}
options={[
{ label: '全部姿态&机位', value: 'all' },
...figureFilterOptions.poses.map((pose) => ({ label: pose, value: pose })),
]}
onChange={(value) => setFigureFilters((current) => ({ ...current, pose: value }))}
/>
<Select
value={figureFilters.background}
popupMatchSelectWidth={false}
options={[
{ label: '全部背景', value: 'all' },
...figureFilterOptions.backgrounds.map((background) => ({
label: background,
value: background,
})),
]}
onChange={(value) =>
setFigureFilters((current) => ({ ...current, background: value }))
}
/>
</Space>
<div className='figure-picker-scroll'>
{filteredFigures.length > 0 ? (
<div className='figure-picker-grid'>
{filteredFigures.map((figure) => (
<button
type='button'
key={figure.id}
className={`picker-figure-card ${selectedFigure?.id === figure.id ? 'active' : ''}`}
onClick={() => {
setSelectedFigure(figure)
setPreviewFigureSelected(false)
setFigurePickerOpen(false)
}}
>
<span className='picker-avatar'>
{figure.avatar ? (
<img src={figure.avatar} alt={figure.name} />
) : (
<UserOutlined />
)}
</span>
<strong>{figure.name}</strong>
{selectedFigure?.id === figure.id ? (
<span className='picker-selected-mark'>
<CheckOutlined />
</span>
) : null}
</button>
))}
</div>
) : (
<Empty description='没有符合条件的形象' />
)}
</div>
</>
) : (
<Empty description='暂无可用形象' />
)}
</Modal>
<Modal
open={voicePickerOpen}
title='选择声音'
footer={null}
onCancel={() => setVoicePickerOpen(false)}
width={620}
className='digital-human-picker-modal voice-picker-modal'
>
<div className='picker-tabs' role='tablist' aria-label='声音性别'>
<button
type='button'
className={voiceGender === 'female' ? 'active' : ''}
onClick={() => setVoiceGender('female')}
>
女声
</button>
<button
type='button'
className={voiceGender === 'male' ? 'active' : ''}
onClick={() => setVoiceGender('male')}
>
男声
</button>
</div>
<div className='voice-picker-grid'>
{renderVoiceGrid(voiceGender === 'female' ? femaleVoices : maleVoices)}
</div>
</Modal>
</Modal> </Modal>
) )
} }
import { useState } from 'react'
import { Button, message, Upload } from 'antd'
import { CheckCircleFilled, DeleteOutlined, UploadOutlined } from '@ant-design/icons'
import { uploadFile } from '@/utils/oss'
const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'bmp']
const VIDEO_EXTENSIONS = ['mp4']
const AUDIO_EXTENSIONS = ['wav', 'mp3', 'wma', 'm4a']
function getExtension(file) {
return file.name.split('.').pop()?.toLowerCase() || ''
}
function getFileCategory(file) {
const extension = getExtension(file)
if (file.type.startsWith('image/') || IMAGE_EXTENSIONS.includes(extension)) return 'image'
if (file.type.startsWith('video/') || VIDEO_EXTENSIONS.includes(extension)) return 'video'
if (file.type.startsWith('audio/') || AUDIO_EXTENSIONS.includes(extension)) return 'audio'
return ''
}
function matchesAccept(file, accept) {
const extension = getExtension(file)
const category = getFileCategory(file)
return accept.split(',').some((item) => {
const pattern = item.trim().toLowerCase()
if (pattern.startsWith('.')) return pattern === `.${extension}`
if (pattern.endsWith('/*')) return category === pattern.slice(0, -2)
return file.type.toLowerCase() === pattern
})
}
export default function DigitalHumanUploadField({
label,
value,
onChange,
accept,
maxSizeMB,
maxImageSizeMB,
maxVideoSizeMB,
hint,
uploadHint,
previewType,
filenamePrefix = 'digital-human',
}) {
const [uploading, setUploading] = useState(false)
const displayLabel = label.replace('(可选)', '')
const upload = async ({ file, onSuccess, onError }) => {
if (!matchesAccept(file, accept)) {
const error = new Error('文件格式不符合要求')
message.error(error.message)
onError(error)
return
}
const fileLimit =
getFileCategory(file) === 'image'
? (maxImageSizeMB ?? maxSizeMB)
: (maxVideoSizeMB ?? maxSizeMB)
if (file.size > fileLimit * 1024 * 1024) {
const error = new Error(`文件不能超过 ${fileLimit}MB`)
message.error(error.message)
onError(error)
return
}
setUploading(true)
try {
const url = await uploadFile(file, { filenamePrefix })
onChange(url)
onSuccess({ url })
message.success(`${label}上传成功`)
} catch (error) {
message.error(`${label}上传失败`)
onError(error)
} finally {
setUploading(false)
}
}
if (previewType === 'image') {
return (
<div className='media-field image-media-field'>
<div className='field-label'>{label}</div>
<div className={`image-upload-card ${value ? 'has-value' : ''}`}>
<Upload
className='image-upload-trigger'
accept={accept}
showUploadList={false}
customRequest={upload}
>
<button type='button' className='image-upload-button' disabled={uploading}>
{value ? (
<img src={value} alt={`${label}预览`} />
) : (
<span className='image-upload-icon'>
<UploadOutlined />
</span>
)}
<span className='image-upload-copy'>
<strong>
{uploading
? '正在上传…'
: value
? `已选择${displayLabel}`
: `上传${displayLabel}`}
</strong>
<small>
{value ? '点击更换图片' : uploadHint || `文件不能超过 ${maxSizeMB}MB`}
</small>
</span>
</button>
</Upload>
{value ? (
<Button
type='text'
icon={<DeleteOutlined />}
className='image-upload-remove'
onClick={() => onChange('')}
aria-label={`清除${label}`}
/>
) : null}
</div>
{hint ? <div className='field-hint'>{hint}</div> : null}
</div>
)
}
return (
<div className='media-field'>
<div className='field-label'>{label}</div>
<div className={`teacher-upload ${value ? 'has-value' : ''}`}>
<Upload accept={accept} showUploadList={false} customRequest={upload}>
<Button icon={<UploadOutlined />} loading={uploading}>
{value ? `重新上传${label}` : `上传${label}`}
</Button>
</Upload>
{value ? (
<span className='upload-ready'>
<CheckCircleFilled /> 已准备好
</span>
) : null}
{value ? (
<Button
type='text'
size='small'
icon={<DeleteOutlined />}
onClick={() => onChange('')}
aria-label={`清除${label}`}
>
清除
</Button>
) : null}
</div>
{value && previewType === 'video' ? (
<div className='source-preview video-preview'>
<video src={value} controls preload='metadata' aria-label={`${label}预览`} />
<span>请播放检查画面、时长和声音是否正确</span>
</div>
) : null}
{hint ? <div className='field-hint'>{hint}</div> : null}
</div>
)
}
.digital-human-modal {
--dh-accent: var(--main-color);
--dh-accent-soft: color-mix(in srgb, var(--main-color) 8%, white);
--dh-focus: color-mix(in srgb, var(--main-color) 18%, transparent);
--dh-ink: rgb(0 0 0 / 88%);
--dh-muted: rgb(0 0 0 / 45%);
--dh-line: #d9d9d9;
--dh-canvas: #f8f6f7;
max-width: calc(100vw - 24px);
.ant-modal-content {
overflow: hidden;
border-radius: 10px;
}
.ant-modal-body {
max-height: calc(100vh - 96px);
overflow-y: auto;
overscroll-behavior: contain;
}
.ant-btn-primary {
border-color: var(--dh-accent);
background: var(--dh-accent);
&:not(:disabled):hover,
&:not(:disabled):focus {
border-color: var(--dh-accent);
background: color-mix(in srgb, var(--dh-accent) 88%, black);
}
}
.ant-btn-link {
color: var(--dh-accent);
}
.ant-checkbox-checked .ant-checkbox-inner,
.ant-checkbox-wrapper:not(.ant-checkbox-wrapper-disabled):hover .ant-checkbox-inner {
border-color: var(--dh-accent);
background: var(--dh-accent);
}
.ant-input:focus,
.ant-input-focused,
.ant-input-number-focused,
.ant-select-focused .ant-select-selector {
border-color: var(--dh-accent) !important;
box-shadow: 0 0 0 2px var(--dh-focus) !important;
}
}
.digital-human-modal-title {
display: flex;
flex-direction: column;
gap: 2px;
> span {
color: #1c1820;
font-size: 16px;
font-weight: 600;
}
small {
color: #8f858d;
font-size: 12px;
font-weight: 400;
}
}
.ai-digital-human { .ai-digital-human {
.section-box { color: var(--dh-ink);
.digital-human-workspace-tabs > .ant-tabs-nav {
margin-bottom: 20px;
}
.component-layout {
display: grid;
grid-template-columns: minmax(320px, 360px) minmax(0, 1fr);
gap: 18px;
align-items: start;
&.is-hidden {
display: none;
}
}
.config-column {
min-width: 0;
max-height: calc(100vh - 190px);
padding-right: 6px;
overflow-y: auto;
scrollbar-width: thin;
}
.preview-panel {
position: sticky;
top: 0;
min-width: 0;
padding: 18px;
border-radius: 10px;
background: var(--dh-canvas);
}
.preview-panel-heading {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 14px; margin-bottom: 14px;
padding: 12px;
background: #fff;
border-radius: 8px;
border: 1px solid #f0f0f0;
.section-title { > div {
font-size: 15px; display: flex;
min-width: 0;
flex-direction: column;
gap: 4px;
}
strong {
color: var(--dh-ink);
font-size: 17px;
font-weight: 650;
}
span {
color: var(--dh-muted);
font-size: 12px;
}
.ant-btn {
flex: 0 0 auto;
color: var(--dh-muted);
}
}
.config-section {
margin-bottom: 24px;
h2 {
margin: 0 0 12px;
color: var(--dh-ink);
font-size: 16px;
font-weight: 600; font-weight: 600;
color: #333;
margin-bottom: 10px;
border-left: 4px solid #ab1941;
padding-left: 10px;
} }
} }
.figure-list { .selected-media-card {
display: flex; display: flex;
overflow-x: auto; width: 100%;
min-height: 80px;
align-items: center;
gap: 12px; gap: 12px;
padding: 6px 0; padding: 8px 12px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: #fff;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
transition:
border-color 0.18s ease,
background 0.18s ease;
&::-webkit-scrollbar { &:hover:not(:disabled) {
height: 6px; border-color: var(--dh-accent);
} background: var(--dh-accent-soft);
&::-webkit-scrollbar-thumb {
background: #e8e8e8;
border-radius: 3px;
} }
.figure-item { &:focus-visible {
flex: 0 0 92px; outline: 3px solid var(--dh-focus);
cursor: pointer; outline-offset: 2px;
text-align: center; }
padding: 8px;
border: 2px solid transparent;
border-radius: 12px;
transition: all 0.3s;
&.active { &:disabled {
border-color: #ab1941; cursor: wait;
background: rgba(171, 25, 65, 0.08); opacity: 0.72;
}
} }
.avatar-wrapper { .selected-media-thumb {
display: grid;
width: 72px; width: 72px;
height: 72px; height: 56px;
margin: 0 auto 6px; flex: 0 0 72px;
position: relative; overflow: hidden;
border-radius: 6px;
background: #eee9eb;
color: #9a9298;
font-size: 22px;
place-items: center;
.figure-avatar { img {
width: 72px; display: block;
height: 72px; width: 100%;
border-radius: 50%; height: 100%;
object-fit: cover; object-fit: cover;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); }
background: #f0f0f0;
} }
.avatar-fallback { .selected-media-copy {
width: 72px;
height: 72px;
border-radius: 50%;
background: #f0f0f0;
display: flex; display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 4px;
strong {
overflow: hidden;
color: var(--dh-ink);
font-size: 16px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
> span:not(.voice-tags) {
display: inline-flex;
align-items: center; align-items: center;
justify-content: center; gap: 4px;
font-size: 28px; color: var(--dh-muted);
color: #bfbfbf; font-size: 13px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
} }
.figure-name { .selected-media-action {
font-size: 13px; flex: 0 0 auto;
color: #555; color: #776f76;
white-space: nowrap; font-size: 18px;
overflow: hidden;
text-overflow: ellipsis;
} }
.voice-card {
min-height: 88px;
.selected-media-thumb {
width: 64px;
height: 64px;
flex-basis: 64px;
} }
} }
.voice-selection { .voice-avatar {
.sub-title { background: #f0edef;
font-size: 13px; color: #8d6872;
font-size: 30px;
}
.voice-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
em {
padding: 2px 6px;
border-radius: 5px;
background: #f0edef;
color: #776f76;
font-size: 11px;
font-style: normal;
}
}
.config-collapse {
margin: 0 0 24px;
> .ant-collapse-item > .ant-collapse-header {
align-items: center;
padding: 0 0 12px;
color: var(--dh-ink);
font-size: 16px;
font-weight: 600;
}
> .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box {
padding: 0;
}
}
.field-label {
margin: 0 0 8px;
color: var(--dh-ink);
font-size: 14px;
font-weight: 500; font-weight: 500;
}
.field-hint {
margin-top: 6px;
color: #8f858d;
font-size: 12px;
line-height: 1.5;
}
.full-control {
width: 100%;
margin-bottom: 16px;
}
.range-setting {
margin: 0 0 16px;
.range-label {
margin-bottom: 8px; margin-bottom: 8px;
color: #666; color: var(--dh-ink);
font-size: 14px;
font-weight: 500;
} }
.voice-grid { .range-control {
display: grid; display: grid;
grid-template-columns: repeat(5, 1fr); grid-template-columns: 18px minmax(60px, 1fr) 18px 56px;
gap: 6px; align-items: center;
max-height: 176px; gap: 8px;
overflow-y: auto;
padding-right: 4px;
} }
.voice-item { .range-edge {
color: var(--dh-muted);
font-size: 12px;
text-align: center;
}
.ant-slider {
margin: 4px 6px;
}
.ant-input-number {
width: 56px;
}
}
.script-section {
margin-top: 4px;
.field-label {
margin-bottom: 12px;
}
}
.script-box {
overflow: hidden;
border: 1px solid var(--dh-line);
border-radius: 6px;
background: #fff;
.ant-input {
min-height: 160px;
padding: 8px 11px;
color: var(--dh-ink);
font-size: 14px;
line-height: 1.57;
resize: vertical;
}
.ant-input-show-count-suffix {
padding-right: 11px;
color: #aa9fa6;
font-size: 12px;
}
}
.output-collapse {
margin-top: 0;
}
.output-settings {
display: flex; display: flex;
flex-direction: column;
gap: 4px;
> .ant-input {
margin-top: 8px;
}
.teacher-upload {
margin-top: 6px;
}
> .advanced-field,
> .media-field {
margin-top: 12px;
}
.advanced-field .full-control {
margin-bottom: 0;
}
}
.choice-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
margin-bottom: 16px;
button {
display: grid;
min-height: 64px;
grid-template-columns: 40px 1fr;
align-content: center;
align-items: center; align-items: center;
justify-content: start;
column-gap: 8px;
padding: 6px 10px; padding: 6px 10px;
border: 1px solid #e8e8e8; border: 1px solid var(--dh-line);
border-radius: 6px; border-radius: 6px;
background: #fff;
color: var(--dh-ink);
cursor: pointer; cursor: pointer;
transition: all 0.2s; font-size: 14px;
font-weight: 400;
text-align: left;
&:hover,
&.active { &.active {
border-color: #ab1941; border-color: var(--dh-accent);
background: rgba(171, 25, 65, 0.08); background: var(--dh-accent-soft);
} color: var(--dh-accent);
&:hover {
background: #fafafa;
} }
.v-info { &:focus-visible {
flex: 1; outline: 3px solid var(--dh-focus);
display: flex; outline-offset: 2px;
flex-direction: column; }
overflow: hidden;
.v-name { small {
font-size: 13px; grid-column: 2;
font-weight: 500; color: var(--dh-muted);
font-size: 12px;
font-weight: 400;
}
} }
.v-style {
font-size: 11px;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.ratio-icon {
display: block;
width: 30px;
height: 18px;
grid-row: span 2;
justify-self: center;
border: 2px solid currentColor;
border-radius: 3px;
&.portrait {
width: 18px;
height: 30px;
} }
} }
.template-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 10px;
margin-bottom: 12px;
} }
.upload-status { .template-card {
margin-top: 10px; position: relative;
color: #52c41a; display: flex;
font-size: 13px; min-width: 0;
padding: 7px;
flex-direction: column;
gap: 7px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: #fff;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
&.active {
border-color: var(--dh-accent);
background: var(--dh-accent-soft);
}
} }
.preview-panel { .template-preview {
.results-container {
min-height: 320px;
background: #f7f8fa;
border-radius: 12px;
border: 2px dashed #e1e4e8;
display: flex; display: flex;
height: 100px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 16px; overflow: hidden;
border-radius: 7px;
background: #f5f1f2;
.empty-preview { img {
text-align: center; width: 100%;
color: #abb2bb; height: 100%;
p { object-fit: contain;
font-size: 14px;
margin-top: 16px;
} }
} }
.generating-box { .template-meta {
text-align: center; display: flex;
.gen-text { justify-content: space-between;
font-weight: 600; gap: 6px;
margin: 16px 0 8px;
color: #333; strong {
font-size: 12px;
} }
.gen-hint {
font-size: 13px; small {
color: #999; color: var(--dh-muted);
font-size: 10px;
} }
} }
.final-video-box { .template-selected {
width: 100%; position: absolute;
video { top: 8px;
width: 100%; right: 8px;
color: var(--dh-accent);
}
.inline-hint {
padding: 10px 12px;
border-radius: 8px; border-radius: 8px;
background: #000; background: var(--dh-canvas);
max-height: 300px; color: var(--dh-muted);
font-size: 12px;
line-height: 1.5;
} }
.video-actions {
margin-top: 14px; .media-field {
.field-label {
margin-bottom: 6px;
font-size: 14px;
}
.teacher-upload {
display: flex; display: flex;
flex-direction: column; min-height: 40px;
gap: 10px; align-items: center;
flex-wrap: wrap;
gap: 8px;
padding: 8px;
border: 1px dashed #ddd4d8;
border-radius: 10px;
background: #fff;
&.has-value {
border-style: solid;
border-color: #b7d9c4;
background: #f3faf5;
} }
} }
.field-hint {
margin-top: 6px;
color: #8f858d;
font-size: 12px;
line-height: 1.5;
} }
.submit-btn { .image-upload-card {
height: 44px; position: relative;
font-size: 15px; overflow: hidden;
font-weight: 600; border: 1px dashed var(--dh-line);
border-radius: 8px; border-radius: 8px;
margin-top: 16px; background: #fff;
transition: border-color 0.2s;
&:hover,
&:focus-within {
border-color: var(--dh-accent);
}
&.has-value {
border-style: solid;
}
}
.image-upload-trigger,
.image-upload-trigger .ant-upload {
display: block;
width: 100%;
} }
.image-upload-button {
display: flex;
width: 100%;
min-height: 72px;
align-items: center;
gap: 12px;
padding: 8px 44px 8px 8px;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
text-align: left;
&:disabled {
cursor: wait;
opacity: 0.65;
}
img {
display: block;
width: 88px;
height: 54px;
flex: 0 0 88px;
border-radius: 6px;
background: var(--dh-canvas);
object-fit: cover;
}
}
.image-upload-icon {
display: grid;
width: 48px;
height: 48px;
flex: 0 0 48px;
border-radius: 6px;
background: var(--dh-canvas);
color: var(--dh-muted);
font-size: 20px;
place-items: center;
}
.image-upload-copy {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 4px;
strong {
color: var(--dh-ink);
font-size: 14px;
font-weight: 500;
}
small {
color: var(--dh-muted);
font-size: 12px;
}
}
.image-upload-remove {
position: absolute;
top: 50%;
right: 8px;
color: var(--dh-muted);
transform: translateY(-50%);
&:hover {
color: #ff4d4f;
}
}
.source-preview {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
padding: 8px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: var(--dh-canvas);
img,
video {
display: block;
width: 112px;
height: 72px;
border-radius: 6px;
background: #1d181a;
object-fit: cover;
}
}
}
.baidu-upload-list {
display: flex;
margin-top: 8px;
flex-direction: column;
gap: 4px;
}
.baidu-upload-item {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 4px 6px 4px 10px;
border-radius: 6px;
background: var(--dh-canvas);
span {
overflow: hidden;
font-size: 12px;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.generation-result {
margin-top: 0;
}
.generation-result-heading {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 22px;
margin-bottom: 10px;
color: var(--dh-muted);
font-size: 13px;
strong {
font-weight: 500;
}
}
.preview-stage {
position: relative;
display: grid;
width: 100%;
min-height: 0;
overflow: visible;
background: #fff;
background-position: center;
background-repeat: no-repeat;
background-size: cover;
place-items: center;
&.landscape {
aspect-ratio: 16 / 9;
}
&.portrait {
width: min(100%, 315px);
margin-inline: auto;
aspect-ratio: 9 / 16;
}
}
.preview-avatar {
display: grid;
width: 110px;
height: 138px;
overflow: hidden;
border-radius: 12px;
background: #e5dfe2;
color: #9a9298;
font-size: 34px;
place-items: center;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.empty-generation {
position: relative;
width: 100%;
height: 100%;
min-height: 0;
align-self: stretch;
justify-self: stretch;
}
.preview-canvas-clip {
position: absolute;
z-index: 1;
inset: 0;
overflow: hidden;
pointer-events: none;
}
.preview-image-layer {
position: absolute;
display: grid;
color: #9a9298;
font-size: 34px;
place-items: center;
will-change: left, top, transform;
img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
}
.preview-layer {
position: absolute;
z-index: 2;
display: grid;
width: 100%;
height: 100%;
box-sizing: border-box;
border: 2px solid transparent;
border-radius: 10px;
color: #9a9298;
font-size: 34px;
cursor: pointer;
place-items: center;
touch-action: none;
user-select: none;
will-change: left, top, transform;
&.selected {
border-color: var(--main-color);
cursor: grab;
}
&.selected:active {
cursor: grabbing;
}
.preview-resize-handle {
position: absolute;
z-index: 2;
display: block;
width: 12px;
height: 12px;
border: 2px solid var(--dh-accent);
border-radius: 50%;
background: #fff;
cursor: nwse-resize;
&.nw {
top: -6px;
left: -6px;
cursor: nwse-resize;
}
&.ne {
top: -6px;
right: -6px;
cursor: nesw-resize;
}
&.sw {
bottom: -6px;
left: -6px;
cursor: nesw-resize;
}
&.se {
right: -6px;
bottom: -6px;
cursor: nwse-resize;
}
}
}
.generating-box {
position: absolute;
z-index: 5;
inset: 0;
display: flex;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
gap: 12px;
background: rgb(255 255 255 / 82%);
color: var(--dh-muted);
}
.generated-preview-clip {
position: absolute;
z-index: 3;
inset: 0;
overflow: hidden;
background: #000;
}
.generated-preview-video {
display: block;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
max-width: 100%;
max-height: 100%;
background: #000;
object-fit: contain;
}
.video-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 14px;
}
.generation-submit {
height: 48px;
margin-top: 16px;
border-radius: 10px;
font-size: 16px;
font-weight: 600;
}
.task-history {
margin-top: 24px;
margin-bottom: 0;
padding: 16px;
border: 1px solid var(--dh-line);
border-radius: 14px;
}
.task-history .section-title {
margin-bottom: 12px;
font-size: 17px;
font-weight: 650;
}
.task-list {
display: grid;
gap: 8px;
}
.task-item {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
align-items: center;
gap: 10px;
padding: 9px 12px;
border: 1px solid var(--dh-line);
border-radius: 8px;
background: #fff;
color: inherit;
cursor: pointer;
text-align: left;
&:hover,
&.active {
border-color: var(--dh-accent);
background: var(--dh-accent-soft);
}
}
.task-name {
overflow: hidden;
font-weight: 500;
text-overflow: ellipsis;
white-space: nowrap;
}
.task-time {
color: #8f858d;
font-size: 12px;
}
.customize-layout {
display: grid;
grid-template-columns: minmax(320px, 420px) minmax(0, 1fr);
gap: 18px;
}
.customize-form-card,
.customize-result-card {
padding: 24px;
border: 1px solid var(--dh-line);
border-radius: 10px;
background: #fff;
h2 {
margin: 0;
font-size: 17px;
}
}
.customize-form-card {
> p {
margin: 8px 0 20px;
color: var(--dh-muted);
}
.media-field {
margin-bottom: 24px;
}
}
.customize-result-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
margin-bottom: 20px;
}
.customize-task {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
border-radius: 8px;
background: var(--dh-canvas);
img {
width: 112px;
height: 112px;
flex: 0 0 112px;
border-radius: 8px;
object-fit: cover;
}
> div {
display: flex;
min-width: 0;
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
span,
small {
color: var(--dh-muted);
}
}
.customize-empty {
display: grid;
min-height: 260px;
border-radius: 8px;
background: var(--dh-canvas);
color: var(--dh-muted);
place-items: center;
}
}
@media (max-width: 900px) {
.ai-digital-human {
.component-layout,
.customize-layout {
grid-template-columns: 1fr;
}
.config-column {
max-height: none;
padding-right: 0;
overflow: visible;
}
.preview-panel {
position: static;
}
}
}
.digital-human-picker-modal {
.ant-modal-content {
border-radius: 14px;
}
.ant-modal-body {
max-height: 65vh;
overflow-y: auto;
}
}
.figure-picker-modal .ant-modal-body {
overflow: hidden;
}
.picker-loading {
display: grid;
min-height: 160px;
place-items: center;
}
.figure-picker-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 22px 18px;
}
.figure-picker-scroll {
max-height: 62vh;
overflow-y: auto;
padding: 4px 4px 8px 0;
}
.picker-figure-card {
position: relative;
display: flex;
min-width: 0;
align-items: stretch;
padding: 0;
border: 0;
background: transparent;
color: inherit;
cursor: pointer;
flex-direction: column;
font: inherit;
text-align: center;
&:hover,
&.active {
.picker-avatar {
border-color: var(--main-color);
}
}
&:focus-visible {
outline: 3px solid color-mix(in srgb, var(--main-color) 35%, transparent);
outline-offset: 4px;
}
strong {
display: block;
overflow: hidden;
margin-top: 10px;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.picker-selected-mark {
position: absolute;
top: 10px;
right: 10px;
display: grid;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--main-color);
color: #fff;
place-items: center;
}
.picker-avatar {
display: grid;
width: 100%;
aspect-ratio: 3 / 4;
overflow: hidden;
border-radius: 10px;
border: 2px solid transparent;
background: #f0f0f5;
color: #9a9298;
place-items: center;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
.picker-tabs {
display: flex;
gap: 8px;
margin-bottom: 14px;
button {
min-width: 84px;
padding: 7px 16px;
border: 1px solid #e5dfe2;
border-radius: 8px;
background: #fff;
color: #756c75;
cursor: pointer;
&.active {
border-color: var(--main-color);
background: color-mix(in srgb, var(--main-color) 8%, white);
color: var(--main-color);
}
}
}
.voice-picker-grid {
.voice-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.voice-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border: 1px solid #e5dfe2;
border-radius: 10px;
background: #fff;
cursor: pointer;
&.active {
border-color: var(--main-color);
background: color-mix(in srgb, var(--main-color) 8%, white);
}
}
.voice-picker-avatar {
display: grid;
width: 38px;
height: 38px;
flex: 0 0 38px;
border-radius: 50%;
background: #f0edef;
color: #8d6872;
place-items: center;
}
.v-info {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 2px;
strong,
small {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
small {
color: #8f858d;
font-size: 11px;
}
}
}
@media (max-width: 640px) {
.digital-human-picker-modal {
width: calc(100vw - 24px) !important;
max-width: calc(100vw - 24px);
.figure-picker-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px 10px;
}
}
.digital-human-modal {
width: calc(100vw - 24px) !important;
max-width: calc(100vw - 24px);
.ant-modal-body {
padding: 12px;
}
}
.ai-digital-human {
.selected-media-card {
gap: 12px;
padding: 12px;
border-radius: 14px;
}
.preview-panel {
padding: 14px;
}
.preview-stage,
.generating-box {
min-height: 360px;
}
.final-video-box video {
height: 360px;
}
.selected-media-thumb {
width: 92px;
height: 72px;
flex-basis: 92px;
}
.voice-card .selected-media-thumb {
width: 72px;
height: 72px;
flex-basis: 72px;
}
.selected-media-copy {
strong {
font-size: 18px;
}
> span:not(.voice-tags) {
font-size: 13px;
}
}
.voice-tags em {
font-size: 11px;
}
.range-setting {
.range-control {
grid-template-columns: 18px minmax(60px, 1fr) 18px 64px;
gap: 6px;
}
.ant-input-number {
width: 64px;
}
}
.script-preview-bar {
align-items: stretch;
flex-direction: column;
gap: 12px;
.ant-btn {
width: 100%;
}
}
.choice-grid,
.figure-picker-grid,
.voice-picker-grid .voice-grid {
gap: 10px;
}
.template-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.task-item {
grid-template-columns: minmax(0, 1fr) auto;
.task-time {
display: none;
}
}
}
}
@media (prefers-reduced-motion: reduce) {
.selected-media-card,
.template-card,
.voice-item {
transition: none;
} }
} }
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
"nuqs": "^2.9.0", "nuqs": "^2.9.0",
"react": "^19.2.7", "react": "^19.2.7",
"react-dom": "^19.2.7", "react-dom": "^19.2.7",
"react-router": "^7.4.0", "react-router": "^7.18.2",
"zustand": "^5.0.3" "zustand": "^5.0.3"
}, },
"devDependencies": { "devDependencies": {
...@@ -31,13 +31,13 @@ ...@@ -31,13 +31,13 @@
"@types/node": "^22.13.9", "@types/node": "^22.13.9",
"@types/react": "^19.2.17", "@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3", "@vitejs/plugin-react": "^6.0.5",
"jsdom": "^29.1.1", "jsdom": "^29.1.1",
"oxfmt": "^0.59.0", "oxfmt": "^0.59.0",
"oxlint": "^1.74.0", "oxlint": "^1.74.0",
"typescript": "^7.0.2", "typescript": "^7.0.2",
"vite": "^8.1.5", "vite": "^8.2.1",
"vite-plugin-mkcert": "^1.17.10", "vite-plugin-mkcert": "^2.1.0",
"vitest": "^4.1.10" "vitest": "^4.1.10"
} }
} }
...@@ -18,7 +18,7 @@ importers: ...@@ -18,7 +18,7 @@ importers:
version: 2.2.5 version: 2.2.5
'@ezijing/ai-react': '@ezijing/ai-react':
specifier: ^1.0.40 specifier: ^1.0.40
version: 1.0.44(debug@4.4.3)(react@18.3.1) version: 1.0.44(debug@4.4.3(supports-color@10.2.2))(react@18.3.1)(supports-color@10.2.2)
'@fortaine/fetch-event-source': '@fortaine/fetch-event-source':
specifier: ^3.0.6 specifier: ^3.0.6
version: 3.0.6 version: 3.0.6
...@@ -30,19 +30,19 @@ importers: ...@@ -30,19 +30,19 @@ importers:
version: 5.1.23 version: 5.1.23
'@wangeditor/editor-for-react': '@wangeditor/editor-for-react':
specifier: ^1.0.6 specifier: ^1.0.6
version: 1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ahooks: ahooks:
specifier: ^3.8.0 specifier: ^3.8.0
version: 3.9.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 3.9.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ali-oss: ali-oss:
specifier: ^6.20.0 specifier: ^6.20.0
version: 6.23.0 version: 6.23.0(supports-color@10.2.2)
antd: antd:
specifier: ^5.21.6 specifier: ^5.21.6
version: 5.29.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 5.29.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
axios: axios:
specifier: ^1.18.1 specifier: ^1.18.1
version: 1.18.1(debug@4.4.3) version: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
dayjs: dayjs:
specifier: ^1.11.11 specifier: ^1.11.11
version: 1.11.21 version: 1.11.21
...@@ -50,8 +50,8 @@ importers: ...@@ -50,8 +50,8 @@ importers:
specifier: ^0.0.2-alpha.1 specifier: ^0.0.2-alpha.1
version: 0.0.2-alpha.1 version: 0.0.2-alpha.1
echarts: echarts:
specifier: ^5.4.3 specifier: ^6.1.0
version: 5.6.0 version: 6.1.0
file-saver: file-saver:
specifier: ^2.0.5 specifier: ^2.0.5
version: 2.0.5 version: 2.0.5
...@@ -62,23 +62,23 @@ importers: ...@@ -62,23 +62,23 @@ importers:
specifier: ^3.7.1 specifier: ^3.7.1
version: 3.7.1 version: 3.7.1
js-cookie: js-cookie:
specifier: ^3.0.5 specifier: ^3.0.8
version: 3.0.8 version: 3.0.8
js-md5: js-md5:
specifier: ^0.8.3 specifier: ^0.8.3
version: 0.8.3 version: 0.8.3
lodash-es: lodash-es:
specifier: ^4.17.21 specifier: ^4.18.1
version: 4.18.1 version: 4.18.1
markdown-it: markdown-it:
specifier: ^14.1.0 specifier: ^14.3.0
version: 14.3.0 version: 14.3.0
mathjax: mathjax:
specifier: 3.2.2 specifier: 3.2.2
version: 3.2.2 version: 3.2.2
nanoid: nanoid:
specifier: ^3.3.8 specifier: ^3.3.18
version: 3.3.16 version: 3.3.18
react: react:
specifier: ^18.3.1 specifier: ^18.3.1
version: 18.3.1 version: 18.3.1
...@@ -89,8 +89,8 @@ importers: ...@@ -89,8 +89,8 @@ importers:
specifier: ^4.0.13 specifier: ^4.0.13
version: 4.1.2(react@18.3.1) version: 4.1.2(react@18.3.1)
react-router: react-router:
specifier: ^7.18.1 specifier: ^7.18.2
version: 7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 7.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-use: react-use:
specifier: ^17.6.0 specifier: ^17.6.0
version: 17.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 17.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
...@@ -117,8 +117,8 @@ importers: ...@@ -117,8 +117,8 @@ importers:
specifier: ^18.3.0 specifier: ^18.3.0
version: 18.3.7(@types/react@18.3.31) version: 18.3.7(@types/react@18.3.31)
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: ^6.0.3 specifier: ^6.0.5
version: 6.0.3(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) version: 6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
jsdom: jsdom:
specifier: ^29.1.1 specifier: ^29.1.1
version: 29.1.1 version: 29.1.1
...@@ -135,14 +135,14 @@ importers: ...@@ -135,14 +135,14 @@ importers:
specifier: ^7.0.2 specifier: ^7.0.2
version: 7.0.2 version: 7.0.2
vite: vite:
specifier: ^8.1.5 specifier: ^8.2.1
version: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0) version: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite-plugin-mkcert: vite-plugin-mkcert:
specifier: ^1.17.10 specifier: ^2.1.0
version: 1.17.12(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) version: 2.1.0(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
vitest: vitest:
specifier: ^4.1.10 specifier: ^4.1.10
version: 4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) version: 4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
apps/learning: apps/learning:
dependencies: dependencies:
...@@ -154,19 +154,19 @@ importers: ...@@ -154,19 +154,19 @@ importers:
version: 5.101.2(react@19.2.7) version: 5.101.2(react@19.2.7)
ali-oss: ali-oss:
specifier: ^6.20.0 specifier: ^6.20.0
version: 6.23.0 version: 6.23.0(supports-color@10.2.2)
antd: antd:
specifier: ^6.5.1 specifier: ^6.5.1
version: 6.5.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) version: 6.5.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
axios: axios:
specifier: ^1.8.4 specifier: ^1.8.4
version: 1.18.1(debug@4.4.3) version: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
js-md5: js-md5:
specifier: ^0.8.3 specifier: ^0.8.3
version: 0.8.3 version: 0.8.3
nuqs: nuqs:
specifier: ^2.9.0 specifier: ^2.9.0
version: 2.9.0(react-router-dom@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) version: 2.9.0(react-router-dom@7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
react: react:
specifier: ^19.2.7 specifier: ^19.2.7
version: 19.2.7 version: 19.2.7
...@@ -174,8 +174,8 @@ importers: ...@@ -174,8 +174,8 @@ importers:
specifier: ^19.2.7 specifier: ^19.2.7
version: 19.2.7(react@19.2.7) version: 19.2.7(react@19.2.7)
react-router: react-router:
specifier: ^7.4.0 specifier: ^7.18.2
version: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) version: 7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
zustand: zustand:
specifier: ^5.0.3 specifier: ^5.0.3
version: 5.0.14(@types/react@19.2.17)(immer@9.0.21)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7)) version: 5.0.14(@types/react@19.2.17)(immer@9.0.21)(react@19.2.7)(use-sync-external-store@1.6.0(react@19.2.7))
...@@ -199,8 +199,8 @@ importers: ...@@ -199,8 +199,8 @@ importers:
specifier: ^19.2.3 specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.17) version: 19.2.3(@types/react@19.2.17)
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: ^6.0.3 specifier: ^6.0.5
version: 6.0.3(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) version: 6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
jsdom: jsdom:
specifier: ^29.1.1 specifier: ^29.1.1
version: 29.1.1 version: 29.1.1
...@@ -214,14 +214,14 @@ importers: ...@@ -214,14 +214,14 @@ importers:
specifier: ^7.0.2 specifier: ^7.0.2
version: 7.0.2 version: 7.0.2
vite: vite:
specifier: ^8.1.5 specifier: ^8.2.1
version: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0) version: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
vite-plugin-mkcert: vite-plugin-mkcert:
specifier: ^1.17.10 specifier: ^2.1.0
version: 1.17.12(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) version: 2.1.0(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
vitest: vitest:
specifier: ^4.1.10 specifier: ^4.1.10
version: 4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) version: 4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
packages: packages:
...@@ -367,15 +367,6 @@ packages: ...@@ -367,15 +367,6 @@ packages:
resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
'@emnapi/core@1.11.1':
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
'@emnapi/runtime@1.11.1':
resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
'@emnapi/wasi-threads@1.2.2':
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
'@emotion/hash@0.8.0': '@emotion/hash@0.8.0':
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
...@@ -575,14 +566,8 @@ packages: ...@@ -575,14 +566,8 @@ packages:
'@jridgewell/trace-mapping@0.3.31': '@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@napi-rs/wasm-runtime@1.1.6': '@oxc-project/types@0.144.0':
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==}
peerDependencies:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
'@oxc-project/types@0.139.0':
resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==}
'@oxfmt/binding-android-arm-eabi@0.59.0': '@oxfmt/binding-android-arm-eabi@0.59.0':
resolution: {integrity: sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA==} resolution: {integrity: sha512-bNTnfbuG7sAwb2PakMNaDukx5kXeW9duXOBeWtTOiLz3fXz3q2DlWguufPZ+c2IHEVrRXHD+M4aUgEWm841LDA==}
...@@ -1161,97 +1146,92 @@ packages: ...@@ -1161,97 +1146,92 @@ packages:
peerDependencies: peerDependencies:
react: '>=18.2.0' react: '>=18.2.0'
'@rolldown/binding-android-arm64@1.1.5': '@rolldown/binding-android-arm64@1.2.4':
resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} resolution: {integrity: sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@rolldown/binding-darwin-arm64@1.1.5': '@rolldown/binding-darwin-arm64@1.2.4':
resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} resolution: {integrity: sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@rolldown/binding-darwin-x64@1.1.5': '@rolldown/binding-darwin-x64@1.2.4':
resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} resolution: {integrity: sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@rolldown/binding-freebsd-x64@1.1.5': '@rolldown/binding-freebsd-x64@1.2.4':
resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} resolution: {integrity: sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.1.5': '@rolldown/binding-linux-arm-gnueabihf@1.2.4':
resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} resolution: {integrity: sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.1.5': '@rolldown/binding-linux-arm64-gnu@1.2.4':
resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} resolution: {integrity: sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.1.5': '@rolldown/binding-linux-arm64-musl@1.2.4':
resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} resolution: {integrity: sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl] libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.1.5': '@rolldown/binding-linux-ppc64-gnu@1.2.4':
resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} resolution: {integrity: sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.1.5': '@rolldown/binding-linux-s390x-gnu@1.2.4':
resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} resolution: {integrity: sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.1.5': '@rolldown/binding-linux-x64-gnu@1.2.4':
resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} resolution: {integrity: sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.1.5': '@rolldown/binding-linux-x64-musl@1.2.4':
resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} resolution: {integrity: sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl] libc: [musl]
'@rolldown/binding-openharmony-arm64@1.1.5': '@rolldown/binding-openharmony-arm64@1.2.4':
resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} resolution: {integrity: sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [openharmony] os: [openharmony]
'@rolldown/binding-wasm32-wasi@1.1.5': '@rolldown/binding-win32-arm64-msvc@1.2.4':
resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} resolution: {integrity: sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [wasm32]
'@rolldown/binding-win32-arm64-msvc@1.1.5':
resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@rolldown/binding-win32-x64-msvc@1.1.5': '@rolldown/binding-win32-x64-msvc@1.2.4':
resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} resolution: {integrity: sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
...@@ -1305,9 +1285,6 @@ packages: ...@@ -1305,9 +1285,6 @@ packages:
'@transloadit/prettier-bytes@0.0.7': '@transloadit/prettier-bytes@0.0.7':
resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==} resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==}
'@tybys/wasm-util@0.10.3':
resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
'@types/aria-query@5.0.4': '@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
...@@ -1485,8 +1462,8 @@ packages: ...@@ -1485,8 +1462,8 @@ packages:
peerDependencies: peerDependencies:
'@uppy/core': ^2.3.3 '@uppy/core': ^2.3.3
'@vitejs/plugin-react@6.0.3': '@vitejs/plugin-react@6.0.5':
resolution: {integrity: sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==} resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies: peerDependencies:
'@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0
...@@ -1840,8 +1817,8 @@ packages: ...@@ -1840,8 +1817,8 @@ packages:
easy-formula-editor@0.0.2-alpha.1: easy-formula-editor@0.0.2-alpha.1:
resolution: {integrity: sha512-SoSFappo+aYoKxztK4TlBTOIx4vFVZ487H4hXDM9cRRHbnuUJdLEiaH7yOKAwoWTeXw59KYKRVgvOFNrzU/+2A==} resolution: {integrity: sha512-SoSFappo+aYoKxztK4TlBTOIx4vFVZ487H4hXDM9cRRHbnuUJdLEiaH7yOKAwoWTeXw59KYKRVgvOFNrzU/+2A==}
echarts@5.6.0: echarts@6.1.0:
resolution: {integrity: sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==} resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==}
ee-first@1.1.1: ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
...@@ -2127,78 +2104,78 @@ packages: ...@@ -2127,78 +2104,78 @@ packages:
engines: {node: '>=18'} engines: {node: '>=18'}
hasBin: true hasBin: true
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.33.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
lightningcss-darwin-arm64@1.32.0: lightningcss-darwin-arm64@1.33.0:
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
lightningcss-darwin-x64@1.32.0: lightningcss-darwin-x64@1.33.0:
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
lightningcss-freebsd-x64@1.32.0: lightningcss-freebsd-x64@1.33.0:
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
lightningcss-linux-arm-gnueabihf@1.32.0: lightningcss-linux-arm-gnueabihf@1.33.0:
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
lightningcss-linux-arm64-gnu@1.32.0: lightningcss-linux-arm64-gnu@1.33.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
lightningcss-linux-arm64-musl@1.32.0: lightningcss-linux-arm64-musl@1.33.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl] libc: [musl]
lightningcss-linux-x64-gnu@1.32.0: lightningcss-linux-x64-gnu@1.33.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
lightningcss-linux-x64-musl@1.32.0: lightningcss-linux-x64-musl@1.33.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl] libc: [musl]
lightningcss-win32-arm64-msvc@1.32.0: lightningcss-win32-arm64-msvc@1.33.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
lightningcss-win32-x64-msvc@1.32.0: lightningcss-win32-x64-msvc@1.33.0:
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
lightningcss@1.32.0: lightningcss@1.33.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
linkify-it@5.0.2: linkify-it@5.0.2:
...@@ -2321,8 +2298,8 @@ packages: ...@@ -2321,8 +2298,8 @@ packages:
react: '*' react: '*'
react-dom: '*' react-dom: '*'
nanoid@3.3.16: nanoid@3.3.18:
resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
...@@ -2433,8 +2410,8 @@ packages: ...@@ -2433,8 +2410,8 @@ packages:
platform@1.3.6: platform@1.3.6:
resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==}
postcss@8.5.19: postcss@8.5.26:
resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
preact@10.29.7: preact@10.29.7:
...@@ -2733,15 +2710,15 @@ packages: ...@@ -2733,15 +2710,15 @@ packages:
react-is@19.2.7: react-is@19.2.7:
resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==}
react-router-dom@7.18.1: react-router-dom@7.18.2:
resolution: {integrity: sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==} resolution: {integrity: sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
peerDependencies: peerDependencies:
react: '>=18' react: '>=18'
react-dom: '>=18' react-dom: '>=18'
react-router@7.18.1: react-router@7.18.2:
resolution: {integrity: sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==} resolution: {integrity: sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
peerDependencies: peerDependencies:
react: '>=18' react: '>=18'
...@@ -2784,8 +2761,8 @@ packages: ...@@ -2784,8 +2761,8 @@ packages:
resize-observer-polyfill@1.5.1: resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
rolldown@1.1.5: rolldown@1.2.4:
resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} resolution: {integrity: sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
...@@ -2927,6 +2904,10 @@ packages: ...@@ -2927,6 +2904,10 @@ packages:
stylis@4.4.0: stylis@4.4.0:
resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==}
supports-color@10.2.2:
resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
engines: {node: '>=18'}
symbol-tree@3.2.4: symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
...@@ -3023,6 +3004,10 @@ packages: ...@@ -3023,6 +3004,10 @@ packages:
resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==}
engines: {node: '>=20.18.1'} engines: {node: '>=20.18.1'}
undici@8.10.0:
resolution: {integrity: sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==}
engines: {node: '>=22.19.0'}
unescape@1.0.1: unescape@1.0.1:
resolution: {integrity: sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==} resolution: {integrity: sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
...@@ -3048,19 +3033,19 @@ packages: ...@@ -3048,19 +3033,19 @@ packages:
resolution: {integrity: sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==} resolution: {integrity: sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==}
engines: {node: '>= 0.12.0'} engines: {node: '>= 0.12.0'}
vite-plugin-mkcert@1.17.12: vite-plugin-mkcert@2.1.0:
resolution: {integrity: sha512-HhLyUZnvkKLuk9o+HBaMlGIa6CqPTfLbVlLFBjrkN4mpZ4saYFRfFx5R12a46UxmzWT9dO8fijmblVvCYvCD3A==} resolution: {integrity: sha512-UQS5iyknruwCsvqDPsKRyHIVSVfv/iGdRPmUPaM1JRwNJo8wo/MubzJ66ckWwOLxFU4wW90oZq9GviV87nRHsg==}
engines: {node: '>=v18.0.0'} engines: {node: '>=22.19.0'}
peerDependencies: peerDependencies:
vite: '>=3' vite: '>=3'
vite@8.1.5: vite@8.2.1:
resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0 '@types/node': ^20.19.0 || >=22.12.0
'@vitejs/devtools': ^0.3.0 '@vitejs/devtools': ^0.4.0
esbuild: ^0.27.0 || ^0.28.0 esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0' jiti: '>=1.21.0'
less: ^4.0.0 less: ^4.0.0
...@@ -3188,8 +3173,8 @@ packages: ...@@ -3188,8 +3173,8 @@ packages:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'} engines: {node: '>=0.4'}
zrender@5.6.1: zrender@6.1.0:
resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==} resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==}
zustand@4.5.7: zustand@4.5.7:
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
...@@ -3383,22 +3368,6 @@ snapshots: ...@@ -3383,22 +3368,6 @@ snapshots:
'@csstools/css-tokenizer@4.0.0': {} '@csstools/css-tokenizer@4.0.0': {}
'@emnapi/core@1.11.1':
dependencies:
'@emnapi/wasi-threads': 1.2.2
tslib: 2.8.1
optional: true
'@emnapi/runtime@1.11.1':
dependencies:
tslib: 2.8.1
optional: true
'@emnapi/wasi-threads@1.2.2':
dependencies:
tslib: 2.8.1
optional: true
'@emotion/hash@0.8.0': {} '@emotion/hash@0.8.0': {}
'@emotion/unitless@0.7.5': {} '@emotion/unitless@0.7.5': {}
...@@ -3483,17 +3452,17 @@ snapshots: ...@@ -3483,17 +3452,17 @@ snapshots:
'@exodus/bytes@1.15.1': {} '@exodus/bytes@1.15.1': {}
'@ezijing/ai-core@1.0.44(debug@4.4.3)': '@ezijing/ai-core@1.0.44(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)':
dependencies: dependencies:
axios: 1.18.1(debug@4.4.3) axios: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
eventsource-parser: 3.1.0 eventsource-parser: 3.1.0
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
- supports-color - supports-color
'@ezijing/ai-react@1.0.44(debug@4.4.3)(react@18.3.1)': '@ezijing/ai-react@1.0.44(debug@4.4.3(supports-color@10.2.2))(react@18.3.1)(supports-color@10.2.2)':
dependencies: dependencies:
'@ezijing/ai-core': 1.0.44(debug@4.4.3) '@ezijing/ai-core': 1.0.44(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
react: 18.3.1 react: 18.3.1
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
...@@ -3524,14 +3493,7 @@ snapshots: ...@@ -3524,14 +3493,7 @@ snapshots:
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
optional: true optional: true
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': '@oxc-project/types@0.144.0': {}
dependencies:
'@emnapi/core': 1.11.1
'@emnapi/runtime': 1.11.1
'@tybys/wasm-util': 0.10.3
optional: true
'@oxc-project/types@0.139.0': {}
'@oxfmt/binding-android-arm-eabi@0.59.0': '@oxfmt/binding-android-arm-eabi@0.59.0':
optional: true optional: true
...@@ -4051,53 +4013,46 @@ snapshots: ...@@ -4051,53 +4013,46 @@ snapshots:
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
'@rolldown/binding-android-arm64@1.1.5': '@rolldown/binding-android-arm64@1.2.4':
optional: true optional: true
'@rolldown/binding-darwin-arm64@1.1.5': '@rolldown/binding-darwin-arm64@1.2.4':
optional: true optional: true
'@rolldown/binding-darwin-x64@1.1.5': '@rolldown/binding-darwin-x64@1.2.4':
optional: true optional: true
'@rolldown/binding-freebsd-x64@1.1.5': '@rolldown/binding-freebsd-x64@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.1.5': '@rolldown/binding-linux-arm-gnueabihf@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-arm64-gnu@1.1.5': '@rolldown/binding-linux-arm64-gnu@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-arm64-musl@1.1.5': '@rolldown/binding-linux-arm64-musl@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-ppc64-gnu@1.1.5': '@rolldown/binding-linux-ppc64-gnu@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-s390x-gnu@1.1.5': '@rolldown/binding-linux-s390x-gnu@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-x64-gnu@1.1.5': '@rolldown/binding-linux-x64-gnu@1.2.4':
optional: true optional: true
'@rolldown/binding-linux-x64-musl@1.1.5': '@rolldown/binding-linux-x64-musl@1.2.4':
optional: true optional: true
'@rolldown/binding-openharmony-arm64@1.1.5': '@rolldown/binding-openharmony-arm64@1.2.4':
optional: true optional: true
'@rolldown/binding-wasm32-wasi@1.1.5': '@rolldown/binding-win32-arm64-msvc@1.2.4':
dependencies:
'@emnapi/core': 1.11.1
'@emnapi/runtime': 1.11.1
'@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
optional: true optional: true
'@rolldown/binding-win32-arm64-msvc@1.1.5': '@rolldown/binding-win32-x64-msvc@1.2.4':
optional: true
'@rolldown/binding-win32-x64-msvc@1.1.5':
optional: true optional: true
'@rolldown/pluginutils@1.0.1': {} '@rolldown/pluginutils@1.0.1': {}
...@@ -4159,11 +4114,6 @@ snapshots: ...@@ -4159,11 +4114,6 @@ snapshots:
'@transloadit/prettier-bytes@0.0.7': {} '@transloadit/prettier-bytes@0.0.7': {}
'@tybys/wasm-util@0.10.3':
dependencies:
tslib: 2.8.1
optional: true
'@types/aria-query@5.0.4': {} '@types/aria-query@5.0.4': {}
'@types/chai@5.2.3': '@types/chai@5.2.3':
...@@ -4275,7 +4225,7 @@ snapshots: ...@@ -4275,7 +4225,7 @@ snapshots:
lodash.throttle: 4.1.1 lodash.throttle: 4.1.1
mime-match: 1.0.2 mime-match: 1.0.2
namespace-emitter: 2.0.1 namespace-emitter: 2.0.1
nanoid: 3.3.16 nanoid: 3.3.18
preact: 10.29.7 preact: 10.29.7
transitivePeerDependencies: transitivePeerDependencies:
- preact-render-to-string - preact-render-to-string
...@@ -4291,12 +4241,12 @@ snapshots: ...@@ -4291,12 +4241,12 @@ snapshots:
'@uppy/companion-client': 2.2.2 '@uppy/companion-client': 2.2.2
'@uppy/core': 2.3.4 '@uppy/core': 2.3.4
'@uppy/utils': 4.1.3 '@uppy/utils': 4.1.3
nanoid: 3.3.16 nanoid: 3.3.18
'@vitejs/plugin-react@6.0.3(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))': '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))':
dependencies: dependencies:
'@rolldown/pluginutils': 1.0.1 '@rolldown/pluginutils': 1.0.1
vite: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0) vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
'@vitest/expect@4.1.10': '@vitest/expect@4.1.10':
dependencies: dependencies:
...@@ -4307,13 +4257,13 @@ snapshots: ...@@ -4307,13 +4257,13 @@ snapshots:
chai: 6.2.2 chai: 6.2.2
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
'@vitest/mocker@4.1.10(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))': '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))':
dependencies: dependencies:
'@vitest/spy': 4.1.10 '@vitest/spy': 4.1.10
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.21 magic-string: 0.30.21
optionalDependencies: optionalDependencies:
vite: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0) vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
'@vitest/pretty-format@4.1.10': '@vitest/pretty-format@4.1.10':
dependencies: dependencies:
...@@ -4339,25 +4289,25 @@ snapshots: ...@@ -4339,25 +4289,25 @@ snapshots:
convert-source-map: 2.0.0 convert-source-map: 2.0.0
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
'@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
is-url: 1.2.4 is-url: 1.2.4
lodash.throttle: 4.1.1 lodash.throttle: 4.1.1
nanoid: 3.3.16 nanoid: 3.3.18
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
'@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
prismjs: 1.30.0 prismjs: 1.30.0
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
'@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@types/event-emitter': 0.3.5 '@types/event-emitter': 0.3.5
'@uppy/core': 2.3.4 '@uppy/core': 2.3.4
...@@ -4374,15 +4324,15 @@ snapshots: ...@@ -4374,15 +4324,15 @@ snapshots:
lodash.isequal: 4.5.0 lodash.isequal: 4.5.0
lodash.throttle: 4.1.1 lodash.throttle: 4.1.1
lodash.toarray: 4.4.0 lodash.toarray: 4.4.0
nanoid: 3.3.16 nanoid: 3.3.18
scroll-into-view-if-needed: 2.2.31 scroll-into-view-if-needed: 2.2.31
slate: 0.72.8 slate: 0.72.8
slate-history: 0.66.0(slate@0.72.8) slate-history: 0.66.0(slate@0.72.8)
snabbdom: 3.6.4 snabbdom: 3.6.4
'@wangeditor/editor-for-react@1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@wangeditor/editor-for-react@1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/editor@5.1.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/editor': 5.1.23 '@wangeditor/editor': 5.1.23
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
...@@ -4391,13 +4341,13 @@ snapshots: ...@@ -4391,13 +4341,13 @@ snapshots:
dependencies: dependencies:
'@uppy/core': 2.3.4 '@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
'@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
is-hotkey: 0.2.0 is-hotkey: 0.2.0
lodash.camelcase: 4.3.0 lodash.camelcase: 4.3.0
...@@ -4407,47 +4357,47 @@ snapshots: ...@@ -4407,47 +4357,47 @@ snapshots:
lodash.isequal: 4.5.0 lodash.isequal: 4.5.0
lodash.throttle: 4.1.1 lodash.throttle: 4.1.1
lodash.toarray: 4.4.0 lodash.toarray: 4.4.0
nanoid: 3.3.16 nanoid: 3.3.18
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
transitivePeerDependencies: transitivePeerDependencies:
- preact-render-to-string - preact-render-to-string
'@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
'@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
lodash.isequal: 4.5.0 lodash.isequal: 4.5.0
lodash.throttle: 4.1.1 lodash.throttle: 4.1.1
nanoid: 3.3.16 nanoid: 3.3.18
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
'@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@uppy/core': 2.3.4 '@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
'@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
lodash.foreach: 4.5.0 lodash.foreach: 4.5.0
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
'@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4)': '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4))(dom7@3.0.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)':
dependencies: dependencies:
'@uppy/core': 2.3.4 '@uppy/core': 2.3.4
'@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
'@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.16)(slate@0.72.8)(snabbdom@3.6.4) '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.18)(slate@0.72.8)(snabbdom@3.6.4)
dom7: 3.0.0 dom7: 3.0.0
nanoid: 3.3.16 nanoid: 3.3.18
slate: 0.72.8 slate: 0.72.8
snabbdom: 3.6.4 snabbdom: 3.6.4
...@@ -4458,9 +4408,9 @@ snapshots: ...@@ -4458,9 +4408,9 @@ snapshots:
address@1.2.2: {} address@1.2.2: {}
agent-base@6.0.2: agent-base@6.0.2(supports-color@10.2.2):
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
...@@ -4483,14 +4433,14 @@ snapshots: ...@@ -4483,14 +4433,14 @@ snapshots:
screenfull: 5.2.0 screenfull: 5.2.0
tslib: 2.8.1 tslib: 2.8.1
ali-oss@6.23.0: ali-oss@6.23.0(supports-color@10.2.2):
dependencies: dependencies:
address: 1.2.2 address: 1.2.2
agentkeepalive: 3.5.3 agentkeepalive: 3.5.3
bowser: 1.9.4 bowser: 1.9.4
copy-to: 2.0.1 copy-to: 2.0.1
dateformat: 2.2.0 dateformat: 2.2.0
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
destroy: 1.2.0 destroy: 1.2.0
end-or-error: 1.0.1 end-or-error: 1.0.1
get-ready: 1.0.0 get-ready: 1.0.0
...@@ -4646,11 +4596,11 @@ snapshots: ...@@ -4646,11 +4596,11 @@ snapshots:
asynckit@0.4.0: {} asynckit@0.4.0: {}
axios@1.18.1(debug@4.4.3): axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2):
dependencies: dependencies:
follow-redirects: 1.16.0(debug@4.4.3) follow-redirects: 1.16.0(debug@4.4.3(supports-color@10.2.2))
form-data: 4.0.6 form-data: 4.0.6
https-proxy-agent: 5.0.1 https-proxy-agent: 5.0.1(supports-color@10.2.2)
proxy-from-env: 2.1.0 proxy-from-env: 2.1.0
transitivePeerDependencies: transitivePeerDependencies:
- debug - debug
...@@ -4746,9 +4696,11 @@ snapshots: ...@@ -4746,9 +4696,11 @@ snapshots:
dayjs@1.11.21: {} dayjs@1.11.21: {}
debug@4.4.3: debug@4.4.3(supports-color@10.2.2):
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
optionalDependencies:
supports-color: 10.2.2
decimal.js@10.6.0: {} decimal.js@10.6.0: {}
...@@ -4782,10 +4734,10 @@ snapshots: ...@@ -4782,10 +4734,10 @@ snapshots:
easy-formula-editor@0.0.2-alpha.1: {} easy-formula-editor@0.0.2-alpha.1: {}
echarts@5.6.0: echarts@6.1.0:
dependencies: dependencies:
tslib: 2.3.0 tslib: 2.3.0
zrender: 5.6.1 zrender: 6.1.0
ee-first@1.1.1: {} ee-first@1.1.1: {}
...@@ -4915,9 +4867,9 @@ snapshots: ...@@ -4915,9 +4867,9 @@ snapshots:
file-saver@2.0.5: {} file-saver@2.0.5: {}
follow-redirects@1.16.0(debug@4.4.3): follow-redirects@1.16.0(debug@4.4.3(supports-color@10.2.2)):
optionalDependencies: optionalDependencies:
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
form-data@4.0.6: form-data@4.0.6:
dependencies: dependencies:
...@@ -4984,10 +4936,10 @@ snapshots: ...@@ -4984,10 +4936,10 @@ snapshots:
html-void-elements@2.0.1: {} html-void-elements@2.0.1: {}
https-proxy-agent@5.0.1: https-proxy-agent@5.0.1(supports-color@10.2.2):
dependencies: dependencies:
agent-base: 6.0.2 agent-base: 6.0.2(supports-color@10.2.2)
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
...@@ -5101,54 +5053,54 @@ snapshots: ...@@ -5101,54 +5053,54 @@ snapshots:
needle: 3.5.0 needle: 3.5.0
source-map: 0.6.1 source-map: 0.6.1
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.33.0:
optional: true optional: true
lightningcss-darwin-arm64@1.32.0: lightningcss-darwin-arm64@1.33.0:
optional: true optional: true
lightningcss-darwin-x64@1.32.0: lightningcss-darwin-x64@1.33.0:
optional: true optional: true
lightningcss-freebsd-x64@1.32.0: lightningcss-freebsd-x64@1.33.0:
optional: true optional: true
lightningcss-linux-arm-gnueabihf@1.32.0: lightningcss-linux-arm-gnueabihf@1.33.0:
optional: true optional: true
lightningcss-linux-arm64-gnu@1.32.0: lightningcss-linux-arm64-gnu@1.33.0:
optional: true optional: true
lightningcss-linux-arm64-musl@1.32.0: lightningcss-linux-arm64-musl@1.33.0:
optional: true optional: true
lightningcss-linux-x64-gnu@1.32.0: lightningcss-linux-x64-gnu@1.33.0:
optional: true optional: true
lightningcss-linux-x64-musl@1.32.0: lightningcss-linux-x64-musl@1.33.0:
optional: true optional: true
lightningcss-win32-arm64-msvc@1.32.0: lightningcss-win32-arm64-msvc@1.33.0:
optional: true optional: true
lightningcss-win32-x64-msvc@1.32.0: lightningcss-win32-x64-msvc@1.33.0:
optional: true optional: true
lightningcss@1.32.0: lightningcss@1.33.0:
dependencies: dependencies:
detect-libc: 2.1.2 detect-libc: 2.1.2
optionalDependencies: optionalDependencies:
lightningcss-android-arm64: 1.32.0 lightningcss-android-arm64: 1.33.0
lightningcss-darwin-arm64: 1.32.0 lightningcss-darwin-arm64: 1.33.0
lightningcss-darwin-x64: 1.32.0 lightningcss-darwin-x64: 1.33.0
lightningcss-freebsd-x64: 1.32.0 lightningcss-freebsd-x64: 1.33.0
lightningcss-linux-arm-gnueabihf: 1.32.0 lightningcss-linux-arm-gnueabihf: 1.33.0
lightningcss-linux-arm64-gnu: 1.32.0 lightningcss-linux-arm64-gnu: 1.33.0
lightningcss-linux-arm64-musl: 1.32.0 lightningcss-linux-arm64-musl: 1.33.0
lightningcss-linux-x64-gnu: 1.32.0 lightningcss-linux-x64-gnu: 1.33.0
lightningcss-linux-x64-musl: 1.32.0 lightningcss-linux-x64-musl: 1.33.0
lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-arm64-msvc: 1.33.0
lightningcss-win32-x64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.33.0
linkify-it@5.0.2: linkify-it@5.0.2:
dependencies: dependencies:
...@@ -5254,7 +5206,7 @@ snapshots: ...@@ -5254,7 +5206,7 @@ snapshots:
stacktrace-js: 2.0.2 stacktrace-js: 2.0.2
stylis: 4.4.0 stylis: 4.4.0
nanoid@3.3.16: {} nanoid@3.3.18: {}
needle@3.5.0: needle@3.5.0:
dependencies: dependencies:
...@@ -5266,13 +5218,13 @@ snapshots: ...@@ -5266,13 +5218,13 @@ snapshots:
node-hex@1.0.1: {} node-hex@1.0.1: {}
nuqs@2.9.0(react-router-dom@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): nuqs@2.9.0(react-router-dom@7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react-router@7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7):
dependencies: dependencies:
'@standard-schema/spec': 1.0.0 '@standard-schema/spec': 1.0.0
react: 19.2.7 react: 19.2.7
optionalDependencies: optionalDependencies:
react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router: 7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react-router-dom: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router-dom: 7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
object-assign@4.1.1: {} object-assign@4.1.1: {}
...@@ -5357,9 +5309,9 @@ snapshots: ...@@ -5357,9 +5309,9 @@ snapshots:
platform@1.3.6: {} platform@1.3.6: {}
postcss@8.5.19: postcss@8.5.26:
dependencies: dependencies:
nanoid: 3.3.16 nanoid: 3.3.18
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
...@@ -5737,14 +5689,14 @@ snapshots: ...@@ -5737,14 +5689,14 @@ snapshots:
react-is@19.2.7: {} react-is@19.2.7: {}
react-router-dom@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7): react-router-dom@7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
dependencies: dependencies:
react: 19.2.7 react: 19.2.7
react-dom: 19.2.7(react@19.2.7) react-dom: 19.2.7(react@19.2.7)
react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router: 7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
optional: true optional: true
react-router@7.18.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): react-router@7.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
cookie: 1.1.1 cookie: 1.1.1
react: 18.3.1 react: 18.3.1
...@@ -5752,7 +5704,7 @@ snapshots: ...@@ -5752,7 +5704,7 @@ snapshots:
optionalDependencies: optionalDependencies:
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7): react-router@7.18.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
dependencies: dependencies:
cookie: 1.1.1 cookie: 1.1.1
react: 19.2.7 react: 19.2.7
...@@ -5809,26 +5761,25 @@ snapshots: ...@@ -5809,26 +5761,25 @@ snapshots:
resize-observer-polyfill@1.5.1: {} resize-observer-polyfill@1.5.1: {}
rolldown@1.1.5: rolldown@1.2.4:
dependencies: dependencies:
'@oxc-project/types': 0.139.0 '@oxc-project/types': 0.144.0
'@rolldown/pluginutils': 1.0.1 '@rolldown/pluginutils': 1.0.1
optionalDependencies: optionalDependencies:
'@rolldown/binding-android-arm64': 1.1.5 '@rolldown/binding-android-arm64': 1.2.4
'@rolldown/binding-darwin-arm64': 1.1.5 '@rolldown/binding-darwin-arm64': 1.2.4
'@rolldown/binding-darwin-x64': 1.1.5 '@rolldown/binding-darwin-x64': 1.2.4
'@rolldown/binding-freebsd-x64': 1.1.5 '@rolldown/binding-freebsd-x64': 1.2.4
'@rolldown/binding-linux-arm-gnueabihf': 1.1.5 '@rolldown/binding-linux-arm-gnueabihf': 1.2.4
'@rolldown/binding-linux-arm64-gnu': 1.1.5 '@rolldown/binding-linux-arm64-gnu': 1.2.4
'@rolldown/binding-linux-arm64-musl': 1.1.5 '@rolldown/binding-linux-arm64-musl': 1.2.4
'@rolldown/binding-linux-ppc64-gnu': 1.1.5 '@rolldown/binding-linux-ppc64-gnu': 1.2.4
'@rolldown/binding-linux-s390x-gnu': 1.1.5 '@rolldown/binding-linux-s390x-gnu': 1.2.4
'@rolldown/binding-linux-x64-gnu': 1.1.5 '@rolldown/binding-linux-x64-gnu': 1.2.4
'@rolldown/binding-linux-x64-musl': 1.1.5 '@rolldown/binding-linux-x64-musl': 1.2.4
'@rolldown/binding-openharmony-arm64': 1.1.5 '@rolldown/binding-openharmony-arm64': 1.2.4
'@rolldown/binding-wasm32-wasi': 1.1.5 '@rolldown/binding-win32-arm64-msvc': 1.2.4
'@rolldown/binding-win32-arm64-msvc': 1.1.5 '@rolldown/binding-win32-x64-msvc': 1.2.4
'@rolldown/binding-win32-x64-msvc': 1.1.5
rtl-css-js@1.16.1: rtl-css-js@1.16.1:
dependencies: dependencies:
...@@ -5972,6 +5923,8 @@ snapshots: ...@@ -5972,6 +5923,8 @@ snapshots:
stylis@4.4.0: {} stylis@4.4.0: {}
supports-color@10.2.2: {}
symbol-tree@3.2.4: {} symbol-tree@3.2.4: {}
terser@5.49.0: terser@5.49.0:
...@@ -6066,6 +6019,8 @@ snapshots: ...@@ -6066,6 +6019,8 @@ snapshots:
undici@7.28.0: {} undici@7.28.0: {}
undici@8.10.0: {}
unescape@1.0.1: unescape@1.0.1:
dependencies: dependencies:
extend-shallow: 2.0.1 extend-shallow: 2.0.1
...@@ -6104,20 +6059,19 @@ snapshots: ...@@ -6104,20 +6059,19 @@ snapshots:
mz: 2.7.0 mz: 2.7.0
unescape: 1.0.1 unescape: 1.0.1
vite-plugin-mkcert@1.17.12(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)): vite-plugin-mkcert@2.1.0(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)):
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3(supports-color@10.2.2)
picocolors: 1.1.1 supports-color: 10.2.2
vite: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0) undici: 8.10.0
transitivePeerDependencies: vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
- supports-color
vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0): vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0):
dependencies: dependencies:
lightningcss: 1.32.0 lightningcss: 1.33.0
picomatch: 4.0.5 picomatch: 4.0.5
postcss: 8.5.19 postcss: 8.5.26
rolldown: 1.1.5 rolldown: 1.2.4
tinyglobby: 0.2.17 tinyglobby: 0.2.17
optionalDependencies: optionalDependencies:
'@types/node': 22.20.1 '@types/node': 22.20.1
...@@ -6126,10 +6080,10 @@ snapshots: ...@@ -6126,10 +6080,10 @@ snapshots:
less: 4.6.7 less: 4.6.7
terser: 5.49.0 terser: 5.49.0
vitest@4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)): vitest@4.1.10(@types/node@22.20.1)(jsdom@29.1.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)):
dependencies: dependencies:
'@vitest/expect': 4.1.10 '@vitest/expect': 4.1.10
'@vitest/mocker': 4.1.10(vite@8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)) '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0))
'@vitest/pretty-format': 4.1.10 '@vitest/pretty-format': 4.1.10
'@vitest/runner': 4.1.10 '@vitest/runner': 4.1.10
'@vitest/snapshot': 4.1.10 '@vitest/snapshot': 4.1.10
...@@ -6146,7 +6100,7 @@ snapshots: ...@@ -6146,7 +6100,7 @@ snapshots:
tinyexec: 1.2.4 tinyexec: 1.2.4
tinyglobby: 0.2.17 tinyglobby: 0.2.17
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
vite: 8.1.5(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0) vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(less@4.6.7)(terser@5.49.0)
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 22.20.1 '@types/node': 22.20.1
...@@ -6196,7 +6150,7 @@ snapshots: ...@@ -6196,7 +6150,7 @@ snapshots:
xtend@4.0.2: {} xtend@4.0.2: {}
zrender@5.6.1: zrender@6.1.0:
dependencies: dependencies:
tslib: 2.3.0 tslib: 2.3.0
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论